]> granicus.if.org Git - python/commitdiff
Yet more explicit special case handling to make
authorMark Dickinson <dickinsm@gmail.com>
Sun, 20 Apr 2008 04:13:13 +0000 (04:13 +0000)
committerMark Dickinson <dickinsm@gmail.com>
Sun, 20 Apr 2008 04:13:13 +0000 (04:13 +0000)
math.pow behave on alpha Tru64.  All IEEE 754
special values are now handled directly; only
the finite**finite case is handled by libm.

Lib/test/test_math.py
Modules/mathmodule.c

index 4a54a6ed9ed3360a599182e31b202a270e8ee4f2..7d6ce4a90e173ae2ae6bb23d8274c2b3d52b3004 100644 (file)
@@ -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
index 0e91f8fd6a48604ca59673b3038821342a30b00c..72ee0db17ecedd29dab4562c6f466e3a9bde8735 100644 (file)
@@ -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))