Kaydet (Commit) 9cae178f authored tarafından Mark Dickinson's avatar Mark Dickinson

Issue #3366: Add expm1 function to math module. Thanks Eric Smith for

testing on Windows.
üst 98e3df38
...@@ -164,6 +164,20 @@ Power and logarithmic functions ...@@ -164,6 +164,20 @@ Power and logarithmic functions
Return ``e**x``. 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]) .. function:: log(x[, base])
With one argument, return the natural logarithm of *x* (to base *e*). With one argument, return the natural logarithm of *x* (to base *e*).
......
...@@ -249,3 +249,73 @@ gam0132 gamma -4503599627370495.5 -> 0.0 ...@@ -249,3 +249,73 @@ gam0132 gamma -4503599627370495.5 -> 0.0
-- thanks to loss of accuracy in 1-x -- thanks to loss of accuracy in 1-x
gam0140 gamma -63.349078729022985 -> 4.1777971677761880e-88 gam0140 gamma -63.349078729022985 -> 4.1777971677761880e-88
gam0141 gamma -127.45117632943295 -> 1.1831110896236810e-214 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
...@@ -987,17 +987,16 @@ class MathTests(unittest.TestCase): ...@@ -987,17 +987,16 @@ class MathTests(unittest.TestCase):
if math.isnan(expected) and math.isnan(got): if math.isnan(expected) and math.isnan(got):
continue continue
if not math.isnan(expected) and not math.isnan(got): if not math.isnan(expected) and not math.isnan(got):
# we use different closeness criteria for if fn == 'lgamma':
# different functions. # we use a weaker accuracy test for lgamma;
if fn == 'gamma': # lgamma only achieves an absolute error of
accuracy_failure = ulps_check(expected, got, 20) # a few multiples of the machine accuracy, in
elif fn == 'lgamma': # general.
accuracy_failure = acc_check(expected, got, accuracy_failure = acc_check(expected, got,
rel_err = 5e-15, rel_err = 5e-15,
abs_err = 5e-15) abs_err = 5e-15)
else: else:
raise ValueError("don't know how to check accuracy " accuracy_failure = ulps_check(expected, got, 20)
"for this function")
if accuracy_failure is None: if accuracy_failure is None:
continue continue
......
...@@ -1683,7 +1683,7 @@ Extension Modules ...@@ -1683,7 +1683,7 @@ Extension Modules
- Issue #7078: Set struct.__doc__ from _struct.__doc__. - 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 - 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 outside of the range of [-1, 1] by normalizing the value to within that
......
...@@ -169,7 +169,7 @@ GLHACK=-Dclear=__GLclear ...@@ -169,7 +169,7 @@ GLHACK=-Dclear=__GLclear
#array arraymodule.c # array objects #array arraymodule.c # array objects
#cmath cmathmodule.c # -lm # complex math library functions #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 #_struct _struct.c # binary structure packing/unpacking
#time timemodule.c # -lm # time operations and variables #time timemodule.c # -lm # time operations and variables
#operator operator.c # operator.add() and similar goodies #operator operator.c # operator.add() and similar goodies
......
/* 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;
}
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
...@@ -53,6 +53,7 @@ raised for division by zero and mod by zero. ...@@ -53,6 +53,7 @@ raised for division by zero and mod by zero.
*/ */
#include "Python.h" #include "Python.h"
#include "_math.h"
#include "longintrepr.h" /* just for SHIFT */ #include "longintrepr.h" /* just for SHIFT */
#ifdef _OSF_SOURCE #ifdef _OSF_SOURCE
...@@ -686,6 +687,10 @@ FUNC1(cosh, cosh, 1, ...@@ -686,6 +687,10 @@ FUNC1(cosh, cosh, 1,
"cosh(x)\n\nReturn the hyperbolic cosine of x.") "cosh(x)\n\nReturn the hyperbolic cosine of x.")
FUNC1(exp, exp, 1, FUNC1(exp, exp, 1,
"exp(x)\n\nReturn e raised to the power of x.") "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, FUNC1(fabs, fabs, 0,
"fabs(x)\n\nReturn the absolute value of the float x.") "fabs(x)\n\nReturn the absolute value of the float x.")
FUNC1(floor, floor, 0, FUNC1(floor, floor, 0,
...@@ -1420,6 +1425,7 @@ static PyMethodDef math_methods[] = { ...@@ -1420,6 +1425,7 @@ static PyMethodDef math_methods[] = {
{"cosh", math_cosh, METH_O, math_cosh_doc}, {"cosh", math_cosh, METH_O, math_cosh_doc},
{"degrees", math_degrees, METH_O, math_degrees_doc}, {"degrees", math_degrees, METH_O, math_degrees_doc},
{"exp", math_exp, METH_O, math_exp_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}, {"fabs", math_fabs, METH_O, math_fabs_doc},
{"factorial", math_factorial, METH_O, math_factorial_doc}, {"factorial", math_factorial, METH_O, math_factorial_doc},
{"floor", math_floor, METH_O, math_floor_doc}, {"floor", math_floor, METH_O, math_floor_doc},
......
...@@ -161,6 +161,10 @@ SOURCE=..\..\Modules\_lsprof.c ...@@ -161,6 +161,10 @@ SOURCE=..\..\Modules\_lsprof.c
# End Source File # End Source File
# Begin Source File # Begin Source File
SOURCE=..\..\Modules\_math.c
# End Source File
# Begin Source File
SOURCE=..\..\Modules\_randommodule.c SOURCE=..\..\Modules\_randommodule.c
# End Source File # End Source File
# Begin Source File # Begin Source File
......
...@@ -388,6 +388,9 @@ ...@@ -388,6 +388,9 @@
<File <File
RelativePath="..\..\Modules\_lsprof.c"> RelativePath="..\..\Modules\_lsprof.c">
</File> </File>
<File
RelativePath="..\..\Modules\_math.c">
</File>
<File <File
RelativePath="..\..\Modules\_randommodule.c"> RelativePath="..\..\Modules\_randommodule.c">
</File> </File>
......
...@@ -1026,6 +1026,14 @@ ...@@ -1026,6 +1026,14 @@
RelativePath="..\..\Modules\_lsprof.c" RelativePath="..\..\Modules\_lsprof.c"
> >
</File> </File>
<File
RelativePath="..\..\Modules\_math.c"
>
</File>
<File
RelativePath="..\..\Modules\_math.h"
>
</File>
<File <File
RelativePath="..\..\Modules\_randommodule.c" RelativePath="..\..\Modules\_randommodule.c"
> >
......
...@@ -1026,6 +1026,14 @@ ...@@ -1026,6 +1026,14 @@
RelativePath="..\Modules\_lsprof.c" RelativePath="..\Modules\_lsprof.c"
> >
</File> </File>
<File
RelativePath="..\Modules\_math.c"
>
</File>
<File
RelativePath="..\Modules\_math.h"
>
</File>
<File <File
RelativePath="..\Modules\_randommodule.c" RelativePath="..\Modules\_randommodule.c"
> >
......
...@@ -414,7 +414,7 @@ class PyBuildExt(build_ext): ...@@ -414,7 +414,7 @@ class PyBuildExt(build_ext):
libraries=math_libs) ) libraries=math_libs) )
# math library functions, e.g. sin() # math library functions, e.g. sin()
exts.append( Extension('math', ['mathmodule.c'], exts.append( Extension('math', ['mathmodule.c', '_math.c'],
libraries=math_libs) ) libraries=math_libs) )
# fast string operations implemented in C # fast string operations implemented in C
exts.append( Extension('strop', ['stropmodule.c']) ) exts.append( Extension('strop', ['stropmodule.c']) )
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment