summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTim Peters <tim.peters@gmail.com>2000-10-12 06:10:25 (GMT)
committerTim Peters <tim.peters@gmail.com>2000-10-12 06:10:25 (GMT)
commit1d120619d475b1b892585b812dc0d28bd389d097 (patch)
tree901fe8402cb0b6a210d902a808e8285c6ef8dcff
parentec1722e8d4e7efc8a6e913a06104874d83c7983b (diff)
downloadcpython-1d120619d475b1b892585b812dc0d28bd389d097.zip
cpython-1d120619d475b1b892585b812dc0d28bd389d097.tar.gz
cpython-1d120619d475b1b892585b812dc0d28bd389d097.tar.bz2
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).
-rw-r--r--Lib/test/output/test_math1
-rw-r--r--Lib/test/test_math.py29
-rw-r--r--Modules/mathmodule.c60
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 [] =