]> granicus.if.org Git - python/commitdiff
Stop raising OverflowError on underflows reported by libm (errno==ERANGE and
authorTim Peters <tim.peters@gmail.com>
Thu, 12 Oct 2000 06:10:25 +0000 (06:10 +0000)
committerTim Peters <tim.peters@gmail.com>
Thu, 12 Oct 2000 06:10:25 +0000 (06:10 +0000)
libm result is 0).  Cautiously add a few libm exception test cases:
1. That exp(-huge) returns 0 without exception.
2. That exp(+huge) triggers OverflowError.
3. That sqrt(-1) raises ValueError specifically (apparently under glibc linked
   with -lieee, it was raising OverflowError due to an accident of the way
   mathmodule.c's CHECK() macro happened to deal with Infs and NaNs under gcc).

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

index 2a980003a5cc9fca66dc3f633a2020bfc18dcbdb..bce7ddeffef0775515cfb8b2daf464ab3a72403e 100644 (file)
@@ -24,3 +24,4 @@ sinh
 sqrt
 tan
 tanh
+exceptions
index 6d6bc44953833e8bc2d5745db8b3d757611a0014..1f31dc5794f58cc2fc39cb00c6e62ad7afe10770 100644 (file)
@@ -152,3 +152,32 @@ testit('tan(-pi/4)', math.tan(-math.pi/4), -1)
 print 'tanh'
 testit('tanh(0)', math.tanh(0), 0)
 testit('tanh(1)+tanh(-1)', math.tanh(1)+math.tanh(-1), 0)
+
+print 'exceptions'  # oooooh, *this* is a x-platform gamble!  good luck
+
+try:
+    x = math.exp(-1000000000)
+except:
+    # mathmodule.c is failing to weed out underflows from libm, or
+    # we've got an fp format with huge dynamic range
+    raise TestFailed("underflowing exp() should not have rasied an exception")
+if x != 0:
+    raise TestFailed("underflowing exp() should have returned 0")
+
+# If this fails, probably using a strict IEEE-754 conforming libm, and x
+# is +Inf afterwards.  But Python wants overflows detected by default.
+try:
+    x = math.exp(1000000000)
+except OverflowError:
+    pass
+else:
+    raise TestFailed("overflowing exp() didn't trigger OverflowError")
+
+# If this fails, it could be a puzzle.  One odd possibility is that
+# mathmodule.c's CHECK() macro is getting confused while comparing
+# Inf (HUGE_VAL) to a NaN, and artificially setting errno to ERANGE
+# as a result (and so raising OverflowError instead).
+try:
+    x = math.sqrt(-1.0)
+except ValueError:
+    pass
index c313f35a22ccc5cd15a4007b0e0208bf894e97bd..09f7135077ef7debf5cdfc0cb711d0ffa7396dcc 100644 (file)
@@ -1,4 +1,3 @@
-
 /* Math module -- standard C math library functions, pi and e */
 
 #include "Python.h"
@@ -18,6 +17,11 @@ extern double modf (double, double *);
 #undef HUGE_VAL
 #endif
 
+/* RED_FLAG 12-Oct-2000 Tim
+ * What CHECK does if errno != 0 and x is a NaN is a platform-dependent crap
+ * shoot.  Most (but not all!) platforms will end up setting errno to ERANGE
+ * then, but EDOM is probably better.
+ */
 #ifdef HUGE_VAL
 #define CHECK(x) if (errno != 0) ; \
        else if (-HUGE_VAL <= (x) && (x) <= HUGE_VAL) ; \
@@ -26,17 +30,35 @@ extern double modf (double, double *);
 #define CHECK(x) /* Don't know how to check */
 #endif
 
-static PyObject *
-math_error(void)
+/* Call is_error when errno != 0, and where x is the result libm
+ * returned.  is_error will usually set up an exception and return
+ * true (1), but may return false (0) without setting up an exception.
+ */
+static int
+is_error(double x)
 {
+       int result = 1; /* presumption of guilt */
        if (errno == EDOM)
                PyErr_SetString(PyExc_ValueError, "math domain error");
-       else if (errno == ERANGE)
-               PyErr_SetString(PyExc_OverflowError, "math range error");
+       else if (errno == ERANGE) {
+               /* ANSI C generally requires libm functions to set ERANGE
+                * on overflow, but also generally *allows* them to set
+                * ERANGE on underflow too.  There's no consistency about
+                * the latter across platforms.  Here we suppress the
+                * underflow errors (libm functions should return a zero
+                * on underflow, and +- HUGE_VAL on overflow, so testing
+                * the result for zero suffices to distinguish the cases).
+                */
+               if (x)
+                       PyErr_SetString(PyExc_OverflowError, 
+                                       "math range error");
+               else
+                       result = 0;
+       }
        else
                 /* Unexpected math error */
                PyErr_SetFromErrno(PyExc_ValueError);
-       return NULL;
+       return result;
 }
 
 static PyObject *
@@ -50,8 +72,8 @@ math_1(PyObject *args, double (*func) (double), char *argsfmt)
        x = (*func)(x);
        PyFPE_END_PROTECT(x)
        CHECK(x);
-       if (errno != 0)
-               return math_error();
+       if (errno && is_error(x))
+               return NULL;
        else
                return PyFloat_FromDouble(x);
 }
@@ -67,8 +89,8 @@ math_2(PyObject *args, double (*func) (double, double), char *argsfmt)
        x = (*func)(x, y);
        PyFPE_END_PROTECT(x)
        CHECK(x);
-       if (errno != 0)
-               return math_error();
+       if (errno && is_error(x))
+               return NULL;
        else
                return PyFloat_FromDouble(x);
 }
@@ -143,9 +165,10 @@ math_frexp(PyObject *self, PyObject *args)
        errno = 0;
        x = frexp(x, &i);
        CHECK(x);
-       if (errno != 0)
-               return math_error();
-       return Py_BuildValue("(di)", x, i);
+       if (errno && is_error(x))
+               return NULL;
+       else
+               return Py_BuildValue("(di)", x, i);
 }
 
 static char math_frexp_doc [] =
@@ -168,8 +191,8 @@ math_ldexp(PyObject *self, PyObject *args)
        x = ldexp(x, exp);
        PyFPE_END_PROTECT(x)
        CHECK(x);
-       if (errno != 0)
-               return math_error();
+       if (errno && is_error(x))
+               return NULL;
        else
                return PyFloat_FromDouble(x);
 }
@@ -197,9 +220,10 @@ math_modf(PyObject *self, PyObject *args)
        x = modf(x, &y);
 #endif
        CHECK(x);
-       if (errno != 0)
-               return math_error();
-       return Py_BuildValue("(dd)", x, y);
+       if (errno && is_error(x))
+               return NULL;
+       else
+               return Py_BuildValue("(dd)", x, y);
 }
 
 static char math_modf_doc [] =