Index: PCbuild/pythoncore.vcproj =================================================================== --- PCbuild/pythoncore.vcproj (revision 76855) +++ PCbuild/pythoncore.vcproj (working copy) @@ -1027,6 +1027,14 @@ > + + + + Index: setup.py =================================================================== --- setup.py (revision 76855) +++ setup.py (working copy) @@ -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']) ) Index: Misc/NEWS =================================================================== --- Misc/NEWS (revision 76855) +++ Misc/NEWS (working copy) @@ -1681,7 +1681,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 Index: PC/VS7.1/pythoncore.vcproj =================================================================== --- PC/VS7.1/pythoncore.vcproj (revision 76855) +++ PC/VS7.1/pythoncore.vcproj (working copy) @@ -389,6 +389,9 @@ RelativePath="..\..\Modules\_lsprof.c"> + + + + + + Index: PC/VC6/pythoncore.dsp =================================================================== --- PC/VC6/pythoncore.dsp (revision 76855) +++ PC/VC6/pythoncore.dsp (working copy) @@ -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 Index: Doc/library/math.rst =================================================================== --- Doc/library/math.rst (revision 76855) +++ Doc/library/math.rst (working copy) @@ -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*). Index: Lib/test/test_math.py =================================================================== --- Lib/test/test_math.py (revision 76855) +++ Lib/test/test_math.py (working copy) @@ -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 Index: Lib/test/math_testcases.txt =================================================================== --- Lib/test/math_testcases.txt (revision 76855) +++ Lib/test/math_testcases.txt (working copy) @@ -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 Index: Modules/mathmodule.c =================================================================== --- Modules/mathmodule.c (revision 76855) +++ Modules/mathmodule.c (working copy) @@ -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}, Index: Modules/Setup.dist =================================================================== --- Modules/Setup.dist (revision 76855) +++ Modules/Setup.dist (working copy) @@ -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 Index: Modules/_math.c =================================================================== --- Modules/_math.c (revision 0) +++ Modules/_math.c (revision 0) @@ -0,0 +1,36 @@ +/* Definitions of some C99 math library functions, for those platforms + that don't implement these functions already. */ + +#include +#include + +/* 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 use the formula f(x) = 2*exp(x/2)*sinh(x/2). We + can't use this formula for all x: it loses precision when x becomes + negative enough that exp(x/2) is subnormal, and gives nan when exp(x/2) + becomes 0, so it's only good for x > -700 or so on an IEEE 754 machine. + This formula is also not perfect for subnormal x, where the division + x/2 loses one bit of precision. So... + + For abs(x) < DBL_EPSILON/2.0, we use the fact that expm1(x) = x to + within machine precision: the error in the approximation expm1(x) ~ x + is bounded by x**2/2/(1-abs(x)) < x**2/(2-DBL_EPSILON), and this is + smaller than 0.5ulp(x). + */ + + if (fabs(x) < DBL_EPSILON/2.0) + return x; + else if (fabs(x) < 0.7) + return 2.0 * sinh(x/2.0) * exp(x/2.0); + else + return exp(x) - 1.0; +} Index: Modules/_math.h =================================================================== --- Modules/_math.h (revision 0) +++ Modules/_math.h (revision 0) @@ -0,0 +1,7 @@ +double _Py_expm1(double x); + +#ifdef HAVE_EXPM1 +#define m_expm1 expm1 +#else +#define m_expm1 _Py_expm1 +#endif