[Python-checkins] r76861 - in python/trunk: Doc/library/math.rst Lib/test/math_testcases.txt Lib/test/test_math.py Misc/NEWS Modules/Setup.dist Modules/_math.c Modules/_math.h Modules/mathmodule.c PC/VC6/pythoncore.dsp PC/VS7.1/pythoncore.vcproj PC/VS8.0/pythoncore.vcproj PCbuild/pythoncore.vcproj setup.py

mark.dickinson python-checkins at python.org
Wed Dec 16 21:13:41 CET 2009


Author: mark.dickinson
Date: Wed Dec 16 21:13:40 2009
New Revision: 76861

Log:
Issue #3366: Add expm1 function to math module.  Thanks Eric Smith for
testing on Windows.


Added:
   python/trunk/Modules/_math.c
   python/trunk/Modules/_math.h
Modified:
   python/trunk/Doc/library/math.rst
   python/trunk/Lib/test/math_testcases.txt
   python/trunk/Lib/test/test_math.py
   python/trunk/Misc/NEWS
   python/trunk/Modules/Setup.dist
   python/trunk/Modules/mathmodule.c
   python/trunk/PC/VC6/pythoncore.dsp
   python/trunk/PC/VS7.1/pythoncore.vcproj
   python/trunk/PC/VS8.0/pythoncore.vcproj
   python/trunk/PCbuild/pythoncore.vcproj
   python/trunk/setup.py

Modified: python/trunk/Doc/library/math.rst
==============================================================================
--- python/trunk/Doc/library/math.rst	(original)
+++ python/trunk/Doc/library/math.rst	Wed Dec 16 21:13:40 2009
@@ -164,6 +164,20 @@
    Return ``e**x``.
 
 
+.. function:: expm1(x)
+
+   Return ``e**x - 1``.  For small floats *x*, the subtraction in
+   ``exp(x) - 1`` can result in a significant loss of precision; the
+   :func:`expm1` function provides a way to compute this quantity to
+   full precision::
+
+      >>> from math import exp, expm1
+      >>> exp(1e-5) - 1  # gives result accurate to 11 places
+      1.0000050000069649e-05
+      >>> expm1(1e-5)    # result accurate to full precision
+      1.0000050000166668e-05
+
+
 .. function:: log(x[, base])
 
    With one argument, return the natural logarithm of *x* (to base *e*).

Modified: python/trunk/Lib/test/math_testcases.txt
==============================================================================
--- python/trunk/Lib/test/math_testcases.txt	(original)
+++ python/trunk/Lib/test/math_testcases.txt	Wed Dec 16 21:13:40 2009
@@ -249,3 +249,73 @@
 -- thanks to loss of accuracy in 1-x
 gam0140 gamma -63.349078729022985 -> 4.1777971677761880e-88
 gam0141 gamma -127.45117632943295 -> 1.1831110896236810e-214
+
+-----------------------------------------------------------
+-- expm1: exp(x) - 1, without precision loss for small x --
+-----------------------------------------------------------
+
+-- special values
+expm10000 expm1 0.0 -> 0.0
+expm10001 expm1 -0.0 -> -0.0
+expm10002 expm1 inf -> inf
+expm10003 expm1 -inf -> -1.0
+expm10004 expm1 nan -> nan
+
+-- expm1(x) ~ x for tiny x
+expm10010 expm1 5e-324 -> 5e-324
+expm10011 expm1 1e-320 -> 1e-320
+expm10012 expm1 1e-300 -> 1e-300
+expm10013 expm1 1e-150 -> 1e-150
+expm10014 expm1 1e-20 -> 1e-20
+
+expm10020 expm1 -5e-324 -> -5e-324
+expm10021 expm1 -1e-320 -> -1e-320
+expm10022 expm1 -1e-300 -> -1e-300
+expm10023 expm1 -1e-150 -> -1e-150
+expm10024 expm1 -1e-20 -> -1e-20
+
+-- moderate sized values, where direct evaluation runs into trouble
+expm10100 expm1 1e-10 -> 1.0000000000500000e-10
+expm10101 expm1 -9.9999999999999995e-08 -> -9.9999995000000163e-8
+expm10102 expm1 3.0000000000000001e-05 -> 3.0000450004500034e-5
+expm10103 expm1 -0.0070000000000000001 -> -0.0069755570667648951
+expm10104 expm1 -0.071499208740094633 -> -0.069002985744820250
+expm10105 expm1 -0.063296004180116799 -> -0.061334416373633009
+expm10106 expm1 0.02390954035597756 -> 0.024197665143819942
+expm10107 expm1 0.085637352649044901 -> 0.089411184580357767
+expm10108 expm1 0.5966174947411006 -> 0.81596588596501485
+expm10109 expm1 0.30247206212075139 -> 0.35319987035848677
+expm10110 expm1 0.74574727375889516 -> 1.1080161116737459
+expm10111 expm1 0.97767512926555711 -> 1.6582689207372185
+expm10112 expm1 0.8450154566787712 -> 1.3280137976535897
+expm10113 expm1 -0.13979260323125264 -> -0.13046144381396060
+expm10114 expm1 -0.52899322039643271 -> -0.41080213643695923
+expm10115 expm1 -0.74083261478900631 -> -0.52328317124797097
+expm10116 expm1 -0.93847766984546055 -> -0.60877704724085946
+expm10117 expm1 10.0 -> 22025.465794806718
+expm10118 expm1 27.0 -> 532048240600.79865
+expm10119 expm1 123 -> 2.6195173187490626e+53
+expm10120 expm1 -12.0 -> -0.99999385578764666
+expm10121 expm1 -35.100000000000001 -> -0.99999999999999944
+
+-- extreme negative values
+expm10201 expm1 -37.0 -> -0.99999999999999989
+expm10200 expm1 -38.0 -> -1.0
+expm10210 expm1 -710.0 -> -1.0
+-- the formula expm1(x) = 2 * sinh(x/2) * exp(x/2) doesn't work so
+-- well when exp(x/2) is subnormal or underflows to zero; check we're
+-- not using it!
+expm10211 expm1 -1420.0 -> -1.0
+expm10212 expm1 -1450.0 -> -1.0
+expm10213 expm1 -1500.0 -> -1.0
+expm10214 expm1 -1e50 -> -1.0
+expm10215 expm1 -1.79e308 -> -1.0
+
+-- extreme positive values
+expm10300 expm1 300 -> 1.9424263952412558e+130
+expm10301 expm1 700 -> 1.0142320547350045e+304
+expm10302 expm1 709.78271289328393 -> 1.7976931346824240e+308
+expm10303 expm1 709.78271289348402 -> inf overflow
+expm10304 expm1 1000 -> inf overflow
+expm10305 expm1 1e50 -> inf overflow
+expm10306 expm1 1.79e308 -> inf overflow

Modified: python/trunk/Lib/test/test_math.py
==============================================================================
--- python/trunk/Lib/test/test_math.py	(original)
+++ python/trunk/Lib/test/test_math.py	Wed Dec 16 21:13:40 2009
@@ -987,17 +987,16 @@
                 if math.isnan(expected) and math.isnan(got):
                     continue
                 if not math.isnan(expected) and not math.isnan(got):
-                    # we use different closeness criteria for
-                    # different functions.
-                    if fn == 'gamma':
-                        accuracy_failure = ulps_check(expected, got, 20)
-                    elif fn == 'lgamma':
+                    if fn == 'lgamma':
+                        # we use a weaker accuracy test for lgamma;
+                        # lgamma only achieves an absolute error of
+                        # a few multiples of the machine accuracy, in
+                        # general.
                         accuracy_failure = acc_check(expected, got,
                                                   rel_err = 5e-15,
                                                   abs_err = 5e-15)
                     else:
-                        raise ValueError("don't know how to check accuracy "
-                                         "for this function")
+                        accuracy_failure = ulps_check(expected, got, 20)
                     if accuracy_failure is None:
                         continue
 

Modified: python/trunk/Misc/NEWS
==============================================================================
--- python/trunk/Misc/NEWS	(original)
+++ python/trunk/Misc/NEWS	Wed Dec 16 21:13:40 2009
@@ -1683,7 +1683,7 @@
 
 - Issue #7078: Set struct.__doc__ from _struct.__doc__.
 
-- Issue #3366: Add gamma, lgamma functions to math module.
+- Issue #3366: Add expm1, gamma, lgamma functions to math module.
 
 - Issue #6823: Allow time.strftime() to accept a tuple with a isdst field
   outside of the range of [-1, 1] by normalizing the value to within that

Modified: python/trunk/Modules/Setup.dist
==============================================================================
--- python/trunk/Modules/Setup.dist	(original)
+++ python/trunk/Modules/Setup.dist	Wed Dec 16 21:13:40 2009
@@ -169,7 +169,7 @@
 
 #array arraymodule.c	# array objects
 #cmath cmathmodule.c # -lm # complex math library functions
-#math mathmodule.c # -lm # math library functions, e.g. sin()
+#math mathmodule.c _math.c # -lm # math library functions, e.g. sin()
 #_struct _struct.c	# binary structure packing/unpacking
 #time timemodule.c # -lm # time operations and variables
 #operator operator.c	# operator.add() and similar goodies

Added: python/trunk/Modules/_math.c
==============================================================================
--- (empty file)
+++ python/trunk/Modules/_math.c	Wed Dec 16 21:13:40 2009
@@ -0,0 +1,31 @@
+/* Definitions of some C99 math library functions, for those platforms
+   that don't implement these functions already. */
+
+#include <float.h>
+#include <math.h>
+
+/* Mathematically, expm1(x) = exp(x) - 1.  The expm1 function is designed
+   to avoid the significant loss of precision that arises from direct
+   evaluation of the expression exp(x) - 1, for x near 0. */
+
+double
+_Py_expm1(double x)
+{
+    /* For abs(x) >= log(2), it's safe to evaluate exp(x) - 1 directly; this
+       also works fine for infinities and nans.
+
+       For smaller x, we can use a method due to Kahan that achieves close to
+       full accuracy.
+    */
+
+    if (fabs(x) < 0.7) {
+        double u;
+        u = exp(x);
+        if (u == 1.0)
+            return x;
+        else
+            return (u - 1.0) * x / log(u);
+    }
+    else
+        return exp(x) - 1.0;
+}

Added: python/trunk/Modules/_math.h
==============================================================================
--- (empty file)
+++ python/trunk/Modules/_math.h	Wed Dec 16 21:13:40 2009
@@ -0,0 +1,9 @@
+double _Py_expm1(double x);
+
+#ifdef HAVE_EXPM1
+#define m_expm1 expm1
+#else
+/* if the system doesn't have expm1, use the substitute
+   function defined in Modules/_math.c. */
+#define m_expm1 _Py_expm1
+#endif

Modified: python/trunk/Modules/mathmodule.c
==============================================================================
--- python/trunk/Modules/mathmodule.c	(original)
+++ python/trunk/Modules/mathmodule.c	Wed Dec 16 21:13:40 2009
@@ -53,6 +53,7 @@
  */
 
 #include "Python.h"
+#include "_math.h"
 #include "longintrepr.h" /* just for SHIFT */
 
 #ifdef _OSF_SOURCE
@@ -686,6 +687,10 @@
       "cosh(x)\n\nReturn the hyperbolic cosine of x.")
 FUNC1(exp, exp, 1,
       "exp(x)\n\nReturn e raised to the power of x.")
+FUNC1(expm1, m_expm1, 1,
+      "expm1(x)\n\nReturn exp(x)-1.\n"
+      "This function avoids the loss of precision involved in the direct "
+      "evaluation of exp(x)-1 for small x.")
 FUNC1(fabs, fabs, 0,
       "fabs(x)\n\nReturn the absolute value of the float x.")
 FUNC1(floor, floor, 0,
@@ -1420,6 +1425,7 @@
 	{"cosh",	math_cosh,	METH_O,		math_cosh_doc},
 	{"degrees",	math_degrees,	METH_O,		math_degrees_doc},
 	{"exp",		math_exp,	METH_O,		math_exp_doc},
+	{"expm1",	math_expm1,	METH_O,		math_expm1_doc},
 	{"fabs",	math_fabs,	METH_O,		math_fabs_doc},
 	{"factorial",	math_factorial,	METH_O,		math_factorial_doc},
 	{"floor",	math_floor,	METH_O,		math_floor_doc},

Modified: python/trunk/PC/VC6/pythoncore.dsp
==============================================================================
--- python/trunk/PC/VC6/pythoncore.dsp	(original)
+++ python/trunk/PC/VC6/pythoncore.dsp	Wed Dec 16 21:13:40 2009
@@ -161,6 +161,10 @@
 # End Source File
 # Begin Source File
 
+SOURCE=..\..\Modules\_math.c
+# End Source File
+# Begin Source File
+
 SOURCE=..\..\Modules\_randommodule.c
 # End Source File
 # Begin Source File

Modified: python/trunk/PC/VS7.1/pythoncore.vcproj
==============================================================================
--- python/trunk/PC/VS7.1/pythoncore.vcproj	(original)
+++ python/trunk/PC/VS7.1/pythoncore.vcproj	Wed Dec 16 21:13:40 2009
@@ -389,6 +389,9 @@
 			RelativePath="..\..\Modules\_lsprof.c">
 		</File>
 		<File
+			RelativePath="..\..\Modules\_math.c">
+		</File>
+		<File
 			RelativePath="..\..\Modules\_randommodule.c">
 		</File>
 		<File

Modified: python/trunk/PC/VS8.0/pythoncore.vcproj
==============================================================================
--- python/trunk/PC/VS8.0/pythoncore.vcproj	(original)
+++ python/trunk/PC/VS8.0/pythoncore.vcproj	Wed Dec 16 21:13:40 2009
@@ -1027,6 +1027,14 @@
 				>
 			</File>
 			<File
+				RelativePath="..\..\Modules\_math.c"
+				>
+			</File>
+			<File
+				RelativePath="..\..\Modules\_math.h"
+				>
+			</File>
+			<File
 				RelativePath="..\..\Modules\_randommodule.c"
 				>
 			</File>

Modified: python/trunk/PCbuild/pythoncore.vcproj
==============================================================================
--- python/trunk/PCbuild/pythoncore.vcproj	(original)
+++ python/trunk/PCbuild/pythoncore.vcproj	Wed Dec 16 21:13:40 2009
@@ -1027,6 +1027,14 @@
 				>
 			</File>
 			<File
+				RelativePath="..\Modules\_math.c"
+				>
+			</File>
+			<File
+				RelativePath="..\Modules\_math.h"
+				>
+			</File>
+			<File
 				RelativePath="..\Modules\_randommodule.c"
 				>
 			</File>

Modified: python/trunk/setup.py
==============================================================================
--- python/trunk/setup.py	(original)
+++ python/trunk/setup.py	Wed Dec 16 21:13:40 2009
@@ -414,7 +414,7 @@
                                libraries=math_libs) )
 
         # math library functions, e.g. sin()
-        exts.append( Extension('math',  ['mathmodule.c'],
+        exts.append( Extension('math',  ['mathmodule.c', '_math.c'],
                                libraries=math_libs) )
         # fast string operations implemented in C
         exts.append( Extension('strop', ['stropmodule.c']) )


More information about the Python-checkins mailing list