Issue #7518: Move substitute definitions of C99 math functions from

pymath.c to Modules/_math.c.
This commit is contained in:
Mark Dickinson 2009-12-21 15:22:00 +00:00
parent 08dca0d6da
commit 12748b003c
8 changed files with 250 additions and 231 deletions

View File

@ -8,9 +8,9 @@ Symbols and macros to supply platform-independent interfaces to mathematical
functions and constants functions and constants
**************************************************************************/ **************************************************************************/
/* Python provides implementations for copysign, acosh, asinh, atanh, /* Python provides implementations for copysign, round and hypot in
* log1p and hypot in Python/pymath.c just in case your math library doesn't * Python/pymath.c just in case your math library doesn't provide the
* provide the functions. * functions.
* *
*Note: PC/pyconfig.h defines copysign as _copysign *Note: PC/pyconfig.h defines copysign as _copysign
*/ */
@ -22,22 +22,6 @@ extern double copysign(double, double);
extern double round(double); extern double round(double);
#endif #endif
#ifndef HAVE_ACOSH
extern double acosh(double);
#endif
#ifndef HAVE_ASINH
extern double asinh(double);
#endif
#ifndef HAVE_ATANH
extern double atanh(double);
#endif
#ifndef HAVE_LOG1P
extern double log1p(double);
#endif
#ifndef HAVE_HYPOT #ifndef HAVE_HYPOT
extern double hypot(double, double); extern double hypot(double, double);
#endif #endif

View File

@ -168,7 +168,7 @@ GLHACK=-Dclear=__GLclear
# Modules that should always be present (non UNIX dependent): # Modules that should always be present (non UNIX dependent):
#array arraymodule.c # array objects #array arraymodule.c # array objects
#cmath cmathmodule.c # -lm # complex math library functions #cmath cmathmodule.c _math.c # -lm # complex math library functions
#math mathmodule.c _math.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

View File

@ -1,8 +1,161 @@
/* Definitions of some C99 math library functions, for those platforms /* Definitions of some C99 math library functions, for those platforms
that don't implement these functions already. */ that don't implement these functions already. */
#include "Python.h"
#include <float.h> #include <float.h>
#include <math.h>
/* The following copyright notice applies to the original
implementations of acosh, asinh and atanh. */
/*
* ====================================================
* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
*
* Developed at SunPro, a Sun Microsystems, Inc. business.
* Permission to use, copy, modify, and distribute this
* software is freely granted, provided that this notice
* is preserved.
* ====================================================
*/
static const double ln2 = 6.93147180559945286227E-01;
static const double two_pow_m28 = 3.7252902984619141E-09; /* 2**-28 */
static const double two_pow_p28 = 268435456.0; /* 2**28 */
static const double zero = 0.0;
/* acosh(x)
* Method :
* Based on
* acosh(x) = log [ x + sqrt(x*x-1) ]
* we have
* acosh(x) := log(x)+ln2, if x is large; else
* acosh(x) := log(2x-1/(sqrt(x*x-1)+x)) if x>2; else
* acosh(x) := log1p(t+sqrt(2.0*t+t*t)); where t=x-1.
*
* Special cases:
* acosh(x) is NaN with signal if x<1.
* acosh(NaN) is NaN without signal.
*/
double
_Py_acosh(double x)
{
if (Py_IS_NAN(x)) {
return x+x;
}
if (x < 1.) { /* x < 1; return a signaling NaN */
errno = EDOM;
#ifdef Py_NAN
return Py_NAN;
#else
return (x-x)/(x-x);
#endif
}
else if (x >= two_pow_p28) { /* x > 2**28 */
if (Py_IS_INFINITY(x)) {
return x+x;
} else {
return log(x)+ln2; /* acosh(huge)=log(2x) */
}
}
else if (x == 1.) {
return 0.0; /* acosh(1) = 0 */
}
else if (x > 2.) { /* 2 < x < 2**28 */
double t = x*x;
return log(2.0*x - 1.0 / (x + sqrt(t - 1.0)));
}
else { /* 1 < x <= 2 */
double t = x - 1.0;
return log1p(t + sqrt(2.0*t + t*t));
}
}
/* asinh(x)
* Method :
* Based on
* asinh(x) = sign(x) * log [ |x| + sqrt(x*x+1) ]
* we have
* asinh(x) := x if 1+x*x=1,
* := sign(x)*(log(x)+ln2)) for large |x|, else
* := sign(x)*log(2|x|+1/(|x|+sqrt(x*x+1))) if|x|>2, else
* := sign(x)*log1p(|x| + x^2/(1 + sqrt(1+x^2)))
*/
double
_Py_asinh(double x)
{
double w;
double absx = fabs(x);
if (Py_IS_NAN(x) || Py_IS_INFINITY(x)) {
return x+x;
}
if (absx < two_pow_m28) { /* |x| < 2**-28 */
return x; /* return x inexact except 0 */
}
if (absx > two_pow_p28) { /* |x| > 2**28 */
w = log(absx)+ln2;
}
else if (absx > 2.0) { /* 2 < |x| < 2**28 */
w = log(2.0*absx + 1.0 / (sqrt(x*x + 1.0) + absx));
}
else { /* 2**-28 <= |x| < 2= */
double t = x*x;
w = log1p(absx + t / (1.0 + sqrt(1.0 + t)));
}
return copysign(w, x);
}
/* atanh(x)
* Method :
* 1.Reduced x to positive by atanh(-x) = -atanh(x)
* 2.For x>=0.5
* 1 2x x
* atanh(x) = --- * log(1 + -------) = 0.5 * log1p(2 * --------)
* 2 1 - x 1 - x
*
* For x<0.5
* atanh(x) = 0.5*log1p(2x+2x*x/(1-x))
*
* Special cases:
* atanh(x) is NaN if |x| >= 1 with signal;
* atanh(NaN) is that NaN with no signal;
*
*/
double
_Py_atanh(double x)
{
double absx;
double t;
if (Py_IS_NAN(x)) {
return x+x;
}
absx = fabs(x);
if (absx >= 1.) { /* |x| >= 1 */
errno = EDOM;
#ifdef Py_NAN
return Py_NAN;
#else
return x/zero;
#endif
}
if (absx < two_pow_m28) { /* |x| < 2**-28 */
return x;
}
if (absx < 0.5) { /* |x| < 0.5 */
t = absx+absx;
t = 0.5 * log1p(t + t*absx / (1.0 - absx));
}
else { /* 0.5 <= |x| <= 1.0 */
t = 0.5 * log1p((absx + absx) / (1.0 - absx));
}
return copysign(t, x);
}
/* Mathematically, expm1(x) = exp(x) - 1. The expm1 function is designed /* Mathematically, expm1(x) = exp(x) - 1. The expm1 function is designed
to avoid the significant loss of precision that arises from direct to avoid the significant loss of precision that arises from direct
@ -29,3 +182,47 @@ _Py_expm1(double x)
else else
return exp(x) - 1.0; return exp(x) - 1.0;
} }
/* log1p(x) = log(1+x). The log1p function is designed to avoid the
significant loss of precision that arises from direct evaluation when x is
small. */
double
_Py_log1p(double x)
{
/* For x small, we use the following approach. Let y be the nearest float
to 1+x, then
1+x = y * (1 - (y-1-x)/y)
so log(1+x) = log(y) + log(1-(y-1-x)/y). Since (y-1-x)/y is tiny, the
second term is well approximated by (y-1-x)/y. If abs(x) >=
DBL_EPSILON/2 or the rounding-mode is some form of round-to-nearest
then y-1-x will be exactly representable, and is computed exactly by
(y-1)-x.
If abs(x) < DBL_EPSILON/2 and the rounding mode is not known to be
round-to-nearest then this method is slightly dangerous: 1+x could be
rounded up to 1+DBL_EPSILON instead of down to 1, and in that case
y-1-x will not be exactly representable any more and the result can be
off by many ulps. But this is easily fixed: for a floating-point
number |x| < DBL_EPSILON/2., the closest floating-point number to
log(1+x) is exactly x.
*/
double y;
if (fabs(x) < DBL_EPSILON/2.) {
return x;
} else if (-0.5 <= x && x <= 1.) {
/* WARNING: it's possible than an overeager compiler
will incorrectly optimize the following two lines
to the equivalent of "return log(1.+x)". If this
happens, then results from log1p will be inaccurate
for small x. */
y = 1.+x;
return log(y)-((y-1.)-x)/y;
} else {
/* NaNs and infinities should end up here */
return log(1.+x);
}
}

View File

@ -1,4 +1,32 @@
double _Py_acosh(double x);
double _Py_asinh(double x);
double _Py_atanh(double x);
double _Py_expm1(double x); double _Py_expm1(double x);
double _Py_log1p(double x);
#ifdef HAVE_ACOSH
#define m_acosh acosh
#else
/* if the system doesn't have acosh, use the substitute
function defined in Modules/_math.c. */
#define m_acosh _Py_acosh
#endif
#ifdef HAVE_ASINH
#define m_asinh asinh
#else
/* if the system doesn't have asinh, use the substitute
function defined in Modules/_math.c. */
#define m_asinh _Py_asinh
#endif
#ifdef HAVE_ATANH
#define m_atanh atanh
#else
/* if the system doesn't have atanh, use the substitute
function defined in Modules/_math.c. */
#define m_atanh _Py_atanh
#endif
#ifdef HAVE_EXPM1 #ifdef HAVE_EXPM1
#define m_expm1 expm1 #define m_expm1 expm1
@ -7,3 +35,11 @@ double _Py_expm1(double x);
function defined in Modules/_math.c. */ function defined in Modules/_math.c. */
#define m_expm1 _Py_expm1 #define m_expm1 _Py_expm1
#endif #endif
#ifdef HAVE_LOG1P
#define m_log1p log1p
#else
/* if the system doesn't have log1p, use the substitute
function defined in Modules/_math.c. */
#define m_log1p _Py_log1p
#endif

View File

@ -3,6 +3,7 @@
/* much code borrowed from mathmodule.c */ /* much code borrowed from mathmodule.c */
#include "Python.h" #include "Python.h"
#include "_math.h"
/* we need DBL_MAX, DBL_MIN, DBL_EPSILON, DBL_MANT_DIG and FLT_RADIX from /* we need DBL_MAX, DBL_MIN, DBL_EPSILON, DBL_MANT_DIG and FLT_RADIX from
float.h. We assume that FLT_RADIX is either 2 or 16. */ float.h. We assume that FLT_RADIX is either 2 or 16. */
#include <float.h> #include <float.h>
@ -149,7 +150,7 @@ c_acos(Py_complex z)
s2.imag = z.imag; s2.imag = z.imag;
s2 = c_sqrt(s2); s2 = c_sqrt(s2);
r.real = 2.*atan2(s1.real, s2.real); r.real = 2.*atan2(s1.real, s2.real);
r.imag = asinh(s2.real*s1.imag - s2.imag*s1.real); r.imag = m_asinh(s2.real*s1.imag - s2.imag*s1.real);
} }
errno = 0; errno = 0;
return r; return r;
@ -181,7 +182,7 @@ c_acosh(Py_complex z)
s2.real = z.real + 1.; s2.real = z.real + 1.;
s2.imag = z.imag; s2.imag = z.imag;
s2 = c_sqrt(s2); s2 = c_sqrt(s2);
r.real = asinh(s1.real*s2.real + s1.imag*s2.imag); r.real = m_asinh(s1.real*s2.real + s1.imag*s2.imag);
r.imag = 2.*atan2(s1.imag, s2.real); r.imag = 2.*atan2(s1.imag, s2.real);
} }
errno = 0; errno = 0;
@ -238,7 +239,7 @@ c_asinh(Py_complex z)
s2.real = 1.-z.imag; s2.real = 1.-z.imag;
s2.imag = z.real; s2.imag = z.real;
s2 = c_sqrt(s2); s2 = c_sqrt(s2);
r.real = asinh(s1.real*s2.imag-s2.real*s1.imag); r.real = m_asinh(s1.real*s2.imag-s2.real*s1.imag);
r.imag = atan2(z.imag, s1.real*s2.real-s1.imag*s2.imag); r.imag = atan2(z.imag, s1.real*s2.real-s1.imag*s2.imag);
} }
errno = 0; errno = 0;
@ -342,7 +343,7 @@ c_atanh(Py_complex z)
errno = 0; errno = 0;
} }
} else { } else {
r.real = log1p(4.*z.real/((1-z.real)*(1-z.real) + ay*ay))/4.; r.real = m_log1p(4.*z.real/((1-z.real)*(1-z.real) + ay*ay))/4.;
r.imag = -atan2(-2.*z.imag, (1-z.real)*(1+z.real) - ay*ay)/2.; r.imag = -atan2(-2.*z.imag, (1-z.real)*(1+z.real) - ay*ay)/2.;
errno = 0; errno = 0;
} }
@ -552,7 +553,7 @@ c_log(Py_complex z)
if (0.71 <= h && h <= 1.73) { if (0.71 <= h && h <= 1.73) {
am = ax > ay ? ax : ay; /* max(ax, ay) */ am = ax > ay ? ax : ay; /* max(ax, ay) */
an = ax > ay ? ay : ax; /* min(ax, ay) */ an = ax > ay ? ay : ax; /* min(ax, ay) */
r.real = log1p((am-1)*(am+1)+an*an)/2.; r.real = m_log1p((am-1)*(am+1)+an*an)/2.;
} else { } else {
r.real = log(h); r.real = log(h);
} }

View File

@ -799,18 +799,18 @@ math_2(PyObject *args, double (*func) (double, double), char *funcname)
FUNC1(acos, acos, 0, FUNC1(acos, acos, 0,
"acos(x)\n\nReturn the arc cosine (measured in radians) of x.") "acos(x)\n\nReturn the arc cosine (measured in radians) of x.")
FUNC1(acosh, acosh, 0, FUNC1(acosh, m_acosh, 0,
"acosh(x)\n\nReturn the hyperbolic arc cosine (measured in radians) of x.") "acosh(x)\n\nReturn the hyperbolic arc cosine (measured in radians) of x.")
FUNC1(asin, asin, 0, FUNC1(asin, asin, 0,
"asin(x)\n\nReturn the arc sine (measured in radians) of x.") "asin(x)\n\nReturn the arc sine (measured in radians) of x.")
FUNC1(asinh, asinh, 0, FUNC1(asinh, m_asinh, 0,
"asinh(x)\n\nReturn the hyperbolic arc sine (measured in radians) of x.") "asinh(x)\n\nReturn the hyperbolic arc sine (measured in radians) of x.")
FUNC1(atan, atan, 0, FUNC1(atan, atan, 0,
"atan(x)\n\nReturn the arc tangent (measured in radians) of x.") "atan(x)\n\nReturn the arc tangent (measured in radians) of x.")
FUNC2(atan2, m_atan2, FUNC2(atan2, m_atan2,
"atan2(y, x)\n\nReturn the arc tangent (measured in radians) of y/x.\n" "atan2(y, x)\n\nReturn the arc tangent (measured in radians) of y/x.\n"
"Unlike atan(y/x), the signs of both x and y are considered.") "Unlike atan(y/x), the signs of both x and y are considered.")
FUNC1(atanh, atanh, 0, FUNC1(atanh, m_atanh, 0,
"atanh(x)\n\nReturn the hyperbolic arc tangent (measured in radians) of x.") "atanh(x)\n\nReturn the hyperbolic arc tangent (measured in radians) of x.")
FUNC1(ceil, ceil, 0, FUNC1(ceil, ceil, 0,
"ceil(x)\n\nReturn the ceiling of x as a float.\n" "ceil(x)\n\nReturn the ceiling of x as a float.\n"
@ -840,7 +840,7 @@ FUNC1A(gamma, m_tgamma,
"gamma(x)\n\nGamma function at x.") "gamma(x)\n\nGamma function at x.")
FUNC1A(lgamma, m_lgamma, FUNC1A(lgamma, m_lgamma,
"lgamma(x)\n\nNatural logarithm of absolute value of Gamma function at x.") "lgamma(x)\n\nNatural logarithm of absolute value of Gamma function at x.")
FUNC1(log1p, log1p, 1, FUNC1(log1p, m_log1p, 1,
"log1p(x)\n\nReturn the natural logarithm of 1+x (base e).\n" "log1p(x)\n\nReturn the natural logarithm of 1+x (base e).\n"
"The result is computed in a way which is accurate for x near zero.") "The result is computed in a way which is accurate for x near zero.")
FUNC1(sin, sin, 0, FUNC1(sin, sin, 0,

View File

@ -77,202 +77,3 @@ round(double x)
return copysign(y, x); return copysign(y, x);
} }
#endif /* HAVE_ROUND */ #endif /* HAVE_ROUND */
#ifndef HAVE_LOG1P
#include <float.h>
double
log1p(double x)
{
/* For x small, we use the following approach. Let y be the nearest
float to 1+x, then
1+x = y * (1 - (y-1-x)/y)
so log(1+x) = log(y) + log(1-(y-1-x)/y). Since (y-1-x)/y is tiny,
the second term is well approximated by (y-1-x)/y. If abs(x) >=
DBL_EPSILON/2 or the rounding-mode is some form of round-to-nearest
then y-1-x will be exactly representable, and is computed exactly
by (y-1)-x.
If abs(x) < DBL_EPSILON/2 and the rounding mode is not known to be
round-to-nearest then this method is slightly dangerous: 1+x could
be rounded up to 1+DBL_EPSILON instead of down to 1, and in that
case y-1-x will not be exactly representable any more and the
result can be off by many ulps. But this is easily fixed: for a
floating-point number |x| < DBL_EPSILON/2., the closest
floating-point number to log(1+x) is exactly x.
*/
double y;
if (fabs(x) < DBL_EPSILON/2.) {
return x;
} else if (-0.5 <= x && x <= 1.) {
/* WARNING: it's possible than an overeager compiler
will incorrectly optimize the following two lines
to the equivalent of "return log(1.+x)". If this
happens, then results from log1p will be inaccurate
for small x. */
y = 1.+x;
return log(y)-((y-1.)-x)/y;
} else {
/* NaNs and infinities should end up here */
return log(1.+x);
}
}
#endif /* HAVE_LOG1P */
/*
* ====================================================
* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
*
* Developed at SunPro, a Sun Microsystems, Inc. business.
* Permission to use, copy, modify, and distribute this
* software is freely granted, provided that this notice
* is preserved.
* ====================================================
*/
static const double ln2 = 6.93147180559945286227E-01;
static const double two_pow_m28 = 3.7252902984619141E-09; /* 2**-28 */
static const double two_pow_p28 = 268435456.0; /* 2**28 */
static const double zero = 0.0;
/* asinh(x)
* Method :
* Based on
* asinh(x) = sign(x) * log [ |x| + sqrt(x*x+1) ]
* we have
* asinh(x) := x if 1+x*x=1,
* := sign(x)*(log(x)+ln2)) for large |x|, else
* := sign(x)*log(2|x|+1/(|x|+sqrt(x*x+1))) if|x|>2, else
* := sign(x)*log1p(|x| + x^2/(1 + sqrt(1+x^2)))
*/
#ifndef HAVE_ASINH
double
asinh(double x)
{
double w;
double absx = fabs(x);
if (Py_IS_NAN(x) || Py_IS_INFINITY(x)) {
return x+x;
}
if (absx < two_pow_m28) { /* |x| < 2**-28 */
return x; /* return x inexact except 0 */
}
if (absx > two_pow_p28) { /* |x| > 2**28 */
w = log(absx)+ln2;
}
else if (absx > 2.0) { /* 2 < |x| < 2**28 */
w = log(2.0*absx + 1.0 / (sqrt(x*x + 1.0) + absx));
}
else { /* 2**-28 <= |x| < 2= */
double t = x*x;
w = log1p(absx + t / (1.0 + sqrt(1.0 + t)));
}
return copysign(w, x);
}
#endif /* HAVE_ASINH */
/* acosh(x)
* Method :
* Based on
* acosh(x) = log [ x + sqrt(x*x-1) ]
* we have
* acosh(x) := log(x)+ln2, if x is large; else
* acosh(x) := log(2x-1/(sqrt(x*x-1)+x)) if x>2; else
* acosh(x) := log1p(t+sqrt(2.0*t+t*t)); where t=x-1.
*
* Special cases:
* acosh(x) is NaN with signal if x<1.
* acosh(NaN) is NaN without signal.
*/
#ifndef HAVE_ACOSH
double
acosh(double x)
{
if (Py_IS_NAN(x)) {
return x+x;
}
if (x < 1.) { /* x < 1; return a signaling NaN */
errno = EDOM;
#ifdef Py_NAN
return Py_NAN;
#else
return (x-x)/(x-x);
#endif
}
else if (x >= two_pow_p28) { /* x > 2**28 */
if (Py_IS_INFINITY(x)) {
return x+x;
} else {
return log(x)+ln2; /* acosh(huge)=log(2x) */
}
}
else if (x == 1.) {
return 0.0; /* acosh(1) = 0 */
}
else if (x > 2.) { /* 2 < x < 2**28 */
double t = x*x;
return log(2.0*x - 1.0 / (x + sqrt(t - 1.0)));
}
else { /* 1 < x <= 2 */
double t = x - 1.0;
return log1p(t + sqrt(2.0*t + t*t));
}
}
#endif /* HAVE_ACOSH */
/* atanh(x)
* Method :
* 1.Reduced x to positive by atanh(-x) = -atanh(x)
* 2.For x>=0.5
* 1 2x x
* atanh(x) = --- * log(1 + -------) = 0.5 * log1p(2 * --------)
* 2 1 - x 1 - x
*
* For x<0.5
* atanh(x) = 0.5*log1p(2x+2x*x/(1-x))
*
* Special cases:
* atanh(x) is NaN if |x| >= 1 with signal;
* atanh(NaN) is that NaN with no signal;
*
*/
#ifndef HAVE_ATANH
double
atanh(double x)
{
double absx;
double t;
if (Py_IS_NAN(x)) {
return x+x;
}
absx = fabs(x);
if (absx >= 1.) { /* |x| >= 1 */
errno = EDOM;
#ifdef Py_NAN
return Py_NAN;
#else
return x/zero;
#endif
}
if (absx < two_pow_m28) { /* |x| < 2**-28 */
return x;
}
if (absx < 0.5) { /* |x| < 0.5 */
t = absx+absx;
t = 0.5 * log1p(t + t*absx / (1.0 - absx));
}
else { /* 0.5 <= |x| <= 1.0 */
t = 0.5 * log1p((absx + absx) / (1.0 - absx));
}
return copysign(t, x);
}
#endif /* HAVE_ATANH */

View File

@ -410,9 +410,9 @@ class PyBuildExt(build_ext):
# array objects # array objects
exts.append( Extension('array', ['arraymodule.c']) ) exts.append( Extension('array', ['arraymodule.c']) )
# complex math library functions # complex math library functions
exts.append( Extension('cmath', ['cmathmodule.c'], exts.append( Extension('cmath', ['cmathmodule.c', '_math.c'],
depends=['_math.h'],
libraries=math_libs) ) libraries=math_libs) )
# math library functions, e.g. sin() # math library functions, e.g. sin()
exts.append( Extension('math', ['mathmodule.c', '_math.c'], exts.append( Extension('math', ['mathmodule.c', '_math.c'],
depends=['_math.h'], depends=['_math.h'],