From: Mark Dickinson Date: Sun, 20 Apr 2008 04:13:13 +0000 (+0000) Subject: Yet more explicit special case handling to make X-Git-Tag: v2.6a3~145 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=cec3f138d8cd6c5e3fd78200119a94d59440cfad;p=python Yet more explicit special case handling to make math.pow behave on alpha Tru64. All IEEE 754 special values are now handled directly; only the finite**finite case is handled by libm. --- diff --git a/Lib/test/test_math.py b/Lib/test/test_math.py index 4a54a6ed9e..7d6ce4a90e 100644 --- a/Lib/test/test_math.py +++ b/Lib/test/test_math.py @@ -498,6 +498,18 @@ class MathTests(unittest.TestCase): self.assertEqual(math.pow(-1.1, INF), INF) self.assertEqual(math.pow(-1.9, INF), INF) + # pow(x, y) should work for x negative, y an integer + self.ftest('(-2.)**3.', math.pow(-2.0, 3.0), -8.0) + self.ftest('(-2.)**2.', math.pow(-2.0, 2.0), 4.0) + self.ftest('(-2.)**1.', math.pow(-2.0, 1.0), -2.0) + self.ftest('(-2.)**0.', math.pow(-2.0, 0.0), 1.0) + self.ftest('(-2.)**-0.', math.pow(-2.0, -0.0), 1.0) + self.ftest('(-2.)**-1.', math.pow(-2.0, -1.0), -0.5) + self.ftest('(-2.)**-2.', math.pow(-2.0, -2.0), 0.25) + self.ftest('(-2.)**-3.', math.pow(-2.0, -3.0), -0.125) + self.assertRaises(ValueError, math.pow, -2.0, -0.5) + self.assertRaises(ValueError, math.pow, -2.0, 0.5) + # the following tests have been commented out since they don't # really belong here: the implementation of ** for floats is # independent of the implemention of math.pow diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c index 0e91f8fd6a..72ee0db17e 100644 --- a/Modules/mathmodule.c +++ b/Modules/mathmodule.c @@ -522,7 +522,7 @@ math_pow(PyObject *self, PyObject *args) { PyObject *ox, *oy; double r, x, y; - int y_is_odd; + int odd_y; if (! PyArg_UnpackTuple(args, "pow", 2, 2, &ox, &oy)) return NULL; @@ -531,53 +531,61 @@ math_pow(PyObject *self, PyObject *args) if ((x == -1.0 || y == -1.0) && PyErr_Occurred()) return NULL; - /* deal directly with various special cases, to cope with problems on - various platforms whose semantics don't exactly match C99 */ - - /* 1**x, x**0, and (-1)**(+-infinity) return 1., even if x is NaN or - an infinity. */ - if (x == 1. || y == 0. || (x == -1. && Py_IS_INFINITY(y))) - return PyFloat_FromDouble(1.); - /* otherwise, return a NaN if either input was a NaN */ - if (Py_IS_NAN(x)) - return PyFloat_FromDouble(x); - if (Py_IS_NAN(y)) - return PyFloat_FromDouble(y); - /* inf ** (nonzero, non-NaN) is one of +-0, +-infinity */ - if (Py_IS_INFINITY(x) && !Py_IS_NAN(y)) { - y_is_odd = Py_IS_FINITE(y) && fmod(fabs(y), 2.0) == 1.0; - if (y > 0.) - r = y_is_odd ? x : fabs(x); - else - r = y_is_odd ? copysign(0., x) : 0.; - return PyFloat_FromDouble(r); - } - - errno = 0; - PyFPE_START_PROTECT("in math_pow", return 0); - r = pow(x, y); - PyFPE_END_PROTECT(r); - if (Py_IS_NAN(r)) { - errno = EDOM; + /* deal directly with IEEE specials, to cope with problems on various + platforms whose semantics don't exactly match C99 */ + if (!Py_IS_FINITE(x) || !Py_IS_FINITE(y)) { + errno = 0; + if (Py_IS_NAN(x)) + r = y == 0. ? 1. : x; /* NaN**0 = 1 */ + else if (Py_IS_NAN(y)) + r = x == 1. ? 1. : y; /* 1**NaN = 1 */ + else if (Py_IS_INFINITY(x)) { + odd_y = Py_IS_FINITE(y) && fmod(fabs(y), 2.0) == 1.0; + if (y > 0.) + r = odd_y ? x : fabs(x); + else if (y == 0.) + r = 1.; + else /* y < 0. */ + r = odd_y ? copysign(0., x) : 0.; + } + else if (Py_IS_INFINITY(y)) { + if (fabs(x) == 1.0) + r = 1.; + else if (y > 0. && fabs(x) > 1.0) + r = y; + else if (y < 0. && fabs(x) < 1.0) { + r = -y; /* result is +inf */ + if (x == 0.) /* 0**-inf: divide-by-zero */ + errno = EDOM; + } + else + r = 0.; + } } - /* an infinite result arises either from: - - (A) (+/-0.)**negative, - (B) overflow of x**y with both x and y finite (and x nonzero) - (C) (+/-inf)**positive, or - (D) x**inf with |x| > 1, or x**-inf with |x| < 1. - - In case (A) we want ValueError to be raised. In case (B) - OverflowError should be raised. In cases (C) and (D) the infinite - result should be returned. - */ - else if (Py_IS_INFINITY(r)) { - if (x == 0.) - errno = EDOM; - else if (Py_IS_FINITE(x) && Py_IS_FINITE(y)) - errno = ERANGE; - else - errno = 0; + else { + /* let libm handle finite**finite */ + errno = 0; + PyFPE_START_PROTECT("in math_pow", return 0); + r = pow(x, y); + PyFPE_END_PROTECT(r); + /* a NaN result should arise only from (-ve)**(finite + non-integer); in this case we want to raise ValueError. */ + if (!Py_IS_FINITE(r)) { + if (Py_IS_NAN(r)) { + errno = EDOM; + } + /* + an infinite result here arises either from: + (A) (+/-0.)**negative (-> divide-by-zero) + (B) overflow of x**y with x and y finite + */ + else if (Py_IS_INFINITY(r)) { + if (x == 0.) + errno = EDOM; + else + errno = ERANGE; + } + } } if (errno && is_error(r))