From 1d120619d475b1b892585b812dc0d28bd389d097 Mon Sep 17 00:00:00 2001 From: Tim Peters Date: Thu, 12 Oct 2000 06:10:25 +0000 Subject: Stop raising OverflowError on underflows reported by libm (errno==ERANGE and 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 | 1 + Lib/test/test_math.py | 29 +++++++++++++++++++++++ Modules/mathmodule.c | 60 +++++++++++++++++++++++++++++++++-------------- 3 files changed, 72 insertions(+), 18 deletions(-) diff --git a/Lib/test/output/test_math b/Lib/test/output/test_math index 2a98000..bce7dde 100644 --- a/Lib/test/output/test_math +++ b/Lib/test/output/test_math @@ -24,3 +24,4 @@ sinh sqrt tan tanh +exceptions diff --git a/Lib/test/test_math.py b/Lib/test/test_math.py index 6d6bc44..1f31dc5 100644 --- a/Lib/test/test_math.py +++ b/Lib/test/test_math.py @@ -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 diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c index c313f35..09f7135 100644 --- a/Modules/mathmodule.c +++ b/Modules/mathmodule.c @@ -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 [] = -- cgit v0.12