diff options
author | Tim Peters <tim.peters@gmail.com> | 2001-09-05 22:36:56 (GMT) |
---|---|---|
committer | Tim Peters <tim.peters@gmail.com> | 2001-09-05 22:36:56 (GMT) |
commit | a40c793d06ee2b42a5013015352616b4ca6b288b (patch) | |
tree | 5e056aac66ded03b3a6cae9118c7e109c861bfc6 | |
parent | 75ed167527e688ab6160af3b387532ea3e1c6a74 (diff) | |
download | cpython-a40c793d06ee2b42a5013015352616b4ca6b288b.zip cpython-a40c793d06ee2b42a5013015352616b4ca6b288b.tar.gz cpython-a40c793d06ee2b42a5013015352616b4ca6b288b.tar.bz2 |
Rework the way we try to check for libm overflow, given that C99 no longer
requires that errno ever get set, and it looks like glibc is already
playing that game. New rules:
+ Never use HUGE_VAL. Use the new Py_HUGE_VAL instead.
+ Never believe errno. If overflow is the only thing you're interested in,
use the new Py_OVERFLOWED(x) macro. If you're interested in any libm
errors, use the new Py_SET_ERANGE_IF_OVERFLOW(x) macro, which attempts
to set errno the way C89 said it worked.
Unfortunately, none of these are reliable, but they work on Windows and I
*expect* under glibc too.
-rw-r--r-- | Include/pyport.h | 33 | ||||
-rw-r--r-- | Lib/test/test_math.py | 2 | ||||
-rw-r--r-- | Misc/NEWS | 7 | ||||
-rw-r--r-- | Modules/cmathmodule.c | 17 | ||||
-rw-r--r-- | Modules/mathmodule.c | 42 | ||||
-rw-r--r-- | Objects/floatobject.c | 17 |
6 files changed, 54 insertions, 64 deletions
diff --git a/Include/pyport.h b/Include/pyport.h index 0363953..15a480a 100644 --- a/Include/pyport.h +++ b/Include/pyport.h @@ -230,6 +230,18 @@ extern "C" { */ #define Py_IS_INFINITY(X) ((X) && (X)*0.5 == (X)) +/* According to + * http://www.cray.com/swpubs/manuals/SN-2194_2.0/html-SN-2194_2.0/x3138.htm + * on some Cray systems HUGE_VAL is incorrectly (according to the C std) + * defined to be the largest positive finite rather than infinity. We need + * the std-conforming infinity meaning (provided the platform has one!). + */ +#ifdef INFINITY +#define Py_HUGE_VAL INFINITY +#else +#define Py_HUGE_VAL HUGE_VAL +#endif + /* Py_OVERFLOWED(X) * Return 1 iff a libm function overflowed. Set errno to 0 before calling * a libm function, and invoke this macro after, passing the function @@ -246,9 +258,24 @@ extern "C" { * in non-overflow cases. * X is evaluated more than once. */ -#define Py_OVERFLOWED(X) ((X) != 0.0 && (errno == ERANGE || \ - (X) == HUGE_VAL || \ - (X) == -HUGE_VAL)) +#define Py_OVERFLOWED(X) ((X) != 0.0 && (errno == ERANGE || \ + (X) == Py_HUGE_VAL || \ + (X) == -Py_HUGE_VAL)) + +/* Py_SET_ERANGE_ON_OVERFLOW(x) + * If a libm function did not set errno, but it looks like the result + * overflowed, set errno to ERANGE. Set errno to 0 before calling a libm + * function, and invoke this macro after, passing the function result. + * Caution: + * This isn't reliable. See Py_OVERFLOWED comments. + * X is evaluated more than once. + */ +#define Py_SET_ERANGE_IF_OVERFLOW(X) \ + do { \ + if (errno == 0 && ((X) == Py_HUGE_VAL || \ + (X) == -Py_HUGE_VAL)) \ + errno = ERANGE; \ + } while(0) /************************************************************************** Prototypes that are missing from the standard include files on some systems diff --git a/Lib/test/test_math.py b/Lib/test/test_math.py index 8419a1f..743d46b 100644 --- a/Lib/test/test_math.py +++ b/Lib/test/test_math.py @@ -181,7 +181,7 @@ def test_exceptions(): 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 + # mathmodule.c's macros are 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: @@ -81,6 +81,13 @@ Core Library +- The new C standard no longer requires that math libraries set errno to + ERANGE on overflow. For platform libraries that exploit this new + freedom, Python's overflow-checking was wholly broken. A new overflow- + checking scheme attempts to repair that, but may not be reliable on all + platforms (C doesn't seem to provide anything both useful and portable + in this area anymore). + - Asynchronous timeout actions are available through the new class threading.Timer. diff --git a/Modules/cmathmodule.c b/Modules/cmathmodule.c index 2cef27c..34b0ce8 100644 --- a/Modules/cmathmodule.c +++ b/Modules/cmathmodule.c @@ -4,19 +4,6 @@ #include "Python.h" -#ifdef i860 -/* Cray APP has bogus definition of HUGE_VAL in <math.h> */ -#undef HUGE_VAL -#endif - -#ifdef HUGE_VAL -#define CHECK(x) if (errno != 0) ; \ - else if (-HUGE_VAL <= (x) && (x) <= HUGE_VAL) ; \ - else errno = ERANGE -#else -#define CHECK(x) /* Don't know how to check */ -#endif - #ifndef M_PI #define M_PI (3.141592653589793239) #endif @@ -366,8 +353,8 @@ math_1(PyObject *args, Py_complex (*func)(Py_complex)) PyFPE_START_PROTECT("complex function", return 0) x = (*func)(x); PyFPE_END_PROTECT(x) - CHECK(x.real); - CHECK(x.imag); + Py_SET_ERANGE_IF_OVERFLOW(x.real); + Py_SET_ERANGE_IF_OVERFLOW(x.imag); if (errno != 0) return math_error(); else diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c index 7f4839a..379fecb 100644 --- a/Modules/mathmodule.c +++ b/Modules/mathmodule.c @@ -12,25 +12,6 @@ extern double modf (double, double *); #endif /* __STDC__ */ #endif /* _MSC_VER */ - -#ifdef i860 -/* Cray APP has bogus definition of HUGE_VAL in <math.h> */ -#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) ; \ - else errno = ERANGE -#else -#define CHECK(x) /* Don't know how to check */ -#endif - #ifdef SCO_ATAN2_BUG /* * UnixWare 7+ is known to have a bug in atan2 that will return PI instead @@ -38,7 +19,7 @@ extern double modf (double, double *); */ static double atan2_sco(double x, double y) { - if (x == 0.0) + if (x == 0.0) return (double)0.0; return atan2(x, y); } @@ -58,14 +39,17 @@ is_error(double x) assert(errno); /* non-zero errno is a precondition for calling */ if (errno == EDOM) PyErr_SetString(PyExc_ValueError, "math domain 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). + * the latter across platforms. + * Alas, C99 never requires that errno be set. + * 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, @@ -89,7 +73,7 @@ math_1(PyObject *args, double (*func) (double), char *argsfmt) PyFPE_START_PROTECT("in math_1", return 0) x = (*func)(x); PyFPE_END_PROTECT(x) - CHECK(x); + Py_SET_ERANGE_IF_OVERFLOW(x); if (errno && is_error(x)) return NULL; else @@ -106,7 +90,7 @@ math_2(PyObject *args, double (*func) (double, double), char *argsfmt) PyFPE_START_PROTECT("in math_2", return 0) x = (*func)(x, y); PyFPE_END_PROTECT(x) - CHECK(x); + Py_SET_ERANGE_IF_OVERFLOW(x); if (errno && is_error(x)) return NULL; else @@ -180,7 +164,7 @@ math_frexp(PyObject *self, PyObject *args) return NULL; errno = 0; x = frexp(x, &i); - CHECK(x); + Py_SET_ERANGE_IF_OVERFLOW(x); if (errno && is_error(x)) return NULL; else @@ -205,7 +189,7 @@ math_ldexp(PyObject *self, PyObject *args) PyFPE_START_PROTECT("ldexp", return 0) x = ldexp(x, exp); PyFPE_END_PROTECT(x) - CHECK(x); + Py_SET_ERANGE_IF_OVERFLOW(x); if (errno && is_error(x)) return NULL; else @@ -231,7 +215,7 @@ math_modf(PyObject *self, PyObject *args) #else x = modf(x, &y); #endif - CHECK(x); + Py_SET_ERANGE_IF_OVERFLOW(x); if (errno && is_error(x)) return NULL; else diff --git a/Objects/floatobject.c b/Objects/floatobject.c index 258c4dd..adeaa9e 100644 --- a/Objects/floatobject.c +++ b/Objects/floatobject.c @@ -8,21 +8,6 @@ #include <ctype.h> -#ifdef i860 -/* Cray APP has bogus definition of HUGE_VAL in <math.h> */ -#undef HUGE_VAL -#endif - -#if defined(HUGE_VAL) && !defined(CHECK) -#define CHECK(x) if (errno != 0) ; \ - else if (-HUGE_VAL <= (x) && (x) <= HUGE_VAL) ; \ - else errno = ERANGE -#endif - -#ifndef CHECK -#define CHECK(x) /* Don't know how to check */ -#endif - #if !defined(__STDC__) && !defined(macintosh) extern double fmod(double, double); extern double pow(double, double); @@ -535,7 +520,7 @@ float_pow(PyObject *v, PyObject *w, PyObject *z) PyFPE_START_PROTECT("pow", return NULL) ix = pow(iv, iw); PyFPE_END_PROTECT(ix) - CHECK(ix); + Py_SET_ERANGE_IF_OVERFLOW(ix); if (errno != 0) { /* XXX could it be another type of error? */ PyErr_SetFromErrno(PyExc_OverflowError); |