mirror of
https://github.com/python/cpython.git
synced 2025-11-02 19:12:55 +00:00
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).
This commit is contained in:
parent
ec1722e8d4
commit
1d120619d4
3 changed files with 72 additions and 18 deletions
|
|
@ -24,3 +24,4 @@ sinh
|
||||||
sqrt
|
sqrt
|
||||||
tan
|
tan
|
||||||
tanh
|
tanh
|
||||||
|
exceptions
|
||||||
|
|
|
||||||
|
|
@ -152,3 +152,32 @@ testit('tan(-pi/4)', math.tan(-math.pi/4), -1)
|
||||||
print 'tanh'
|
print 'tanh'
|
||||||
testit('tanh(0)', math.tanh(0), 0)
|
testit('tanh(0)', math.tanh(0), 0)
|
||||||
testit('tanh(1)+tanh(-1)', math.tanh(1)+math.tanh(-1), 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
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
/* Math module -- standard C math library functions, pi and e */
|
/* Math module -- standard C math library functions, pi and e */
|
||||||
|
|
||||||
#include "Python.h"
|
#include "Python.h"
|
||||||
|
|
@ -18,6 +17,11 @@ extern double modf (double, double *);
|
||||||
#undef HUGE_VAL
|
#undef HUGE_VAL
|
||||||
#endif
|
#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
|
#ifdef HUGE_VAL
|
||||||
#define CHECK(x) if (errno != 0) ; \
|
#define CHECK(x) if (errno != 0) ; \
|
||||||
else if (-HUGE_VAL <= (x) && (x) <= HUGE_VAL) ; \
|
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 */
|
#define CHECK(x) /* Don't know how to check */
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static PyObject *
|
/* Call is_error when errno != 0, and where x is the result libm
|
||||||
math_error(void)
|
* 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)
|
if (errno == EDOM)
|
||||||
PyErr_SetString(PyExc_ValueError, "math domain error");
|
PyErr_SetString(PyExc_ValueError, "math domain error");
|
||||||
else if (errno == ERANGE)
|
else if (errno == ERANGE) {
|
||||||
PyErr_SetString(PyExc_OverflowError, "math range error");
|
/* 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
|
else
|
||||||
/* Unexpected math error */
|
/* Unexpected math error */
|
||||||
PyErr_SetFromErrno(PyExc_ValueError);
|
PyErr_SetFromErrno(PyExc_ValueError);
|
||||||
return NULL;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
|
|
@ -50,8 +72,8 @@ math_1(PyObject *args, double (*func) (double), char *argsfmt)
|
||||||
x = (*func)(x);
|
x = (*func)(x);
|
||||||
PyFPE_END_PROTECT(x)
|
PyFPE_END_PROTECT(x)
|
||||||
CHECK(x);
|
CHECK(x);
|
||||||
if (errno != 0)
|
if (errno && is_error(x))
|
||||||
return math_error();
|
return NULL;
|
||||||
else
|
else
|
||||||
return PyFloat_FromDouble(x);
|
return PyFloat_FromDouble(x);
|
||||||
}
|
}
|
||||||
|
|
@ -67,8 +89,8 @@ math_2(PyObject *args, double (*func) (double, double), char *argsfmt)
|
||||||
x = (*func)(x, y);
|
x = (*func)(x, y);
|
||||||
PyFPE_END_PROTECT(x)
|
PyFPE_END_PROTECT(x)
|
||||||
CHECK(x);
|
CHECK(x);
|
||||||
if (errno != 0)
|
if (errno && is_error(x))
|
||||||
return math_error();
|
return NULL;
|
||||||
else
|
else
|
||||||
return PyFloat_FromDouble(x);
|
return PyFloat_FromDouble(x);
|
||||||
}
|
}
|
||||||
|
|
@ -143,9 +165,10 @@ math_frexp(PyObject *self, PyObject *args)
|
||||||
errno = 0;
|
errno = 0;
|
||||||
x = frexp(x, &i);
|
x = frexp(x, &i);
|
||||||
CHECK(x);
|
CHECK(x);
|
||||||
if (errno != 0)
|
if (errno && is_error(x))
|
||||||
return math_error();
|
return NULL;
|
||||||
return Py_BuildValue("(di)", x, i);
|
else
|
||||||
|
return Py_BuildValue("(di)", x, i);
|
||||||
}
|
}
|
||||||
|
|
||||||
static char math_frexp_doc [] =
|
static char math_frexp_doc [] =
|
||||||
|
|
@ -168,8 +191,8 @@ math_ldexp(PyObject *self, PyObject *args)
|
||||||
x = ldexp(x, exp);
|
x = ldexp(x, exp);
|
||||||
PyFPE_END_PROTECT(x)
|
PyFPE_END_PROTECT(x)
|
||||||
CHECK(x);
|
CHECK(x);
|
||||||
if (errno != 0)
|
if (errno && is_error(x))
|
||||||
return math_error();
|
return NULL;
|
||||||
else
|
else
|
||||||
return PyFloat_FromDouble(x);
|
return PyFloat_FromDouble(x);
|
||||||
}
|
}
|
||||||
|
|
@ -197,9 +220,10 @@ math_modf(PyObject *self, PyObject *args)
|
||||||
x = modf(x, &y);
|
x = modf(x, &y);
|
||||||
#endif
|
#endif
|
||||||
CHECK(x);
|
CHECK(x);
|
||||||
if (errno != 0)
|
if (errno && is_error(x))
|
||||||
return math_error();
|
return NULL;
|
||||||
return Py_BuildValue("(dd)", x, y);
|
else
|
||||||
|
return Py_BuildValue("(dd)", x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
static char math_modf_doc [] =
|
static char math_modf_doc [] =
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue