]> granicus.if.org Git - python/commitdiff
Rework the way we try to check for libm overflow, given that C99 no longer
authorTim Peters <tim.peters@gmail.com>
Wed, 5 Sep 2001 22:36:56 +0000 (22:36 +0000)
committerTim Peters <tim.peters@gmail.com>
Wed, 5 Sep 2001 22:36:56 +0000 (22:36 +0000)
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.

Include/pyport.h
Lib/test/test_math.py
Misc/NEWS
Modules/cmathmodule.c
Modules/mathmodule.c
Objects/floatobject.c

index 0363953654b1c124c68d0f62bf45afa693483599..15a480a8bbea519e1fdbc2e17b37393221ad5e12 100644 (file)
@@ -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
index 8419a1f93b8e74ddede7432c3ce84bb241905255..743d46b3b4935d3590ff5af69f3a446e9fe51c56 100644 (file)
@@ -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:
index 02d49687604fc36d43c29899dee3e9d83a4f5b49..50ec64f581315e643c42d6129ee63da9f26e3c91 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -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.
 
index 2cef27cd30bd3729b99155e42659e5db77708f0e..34b0ce85f31fe11b4f09d0e3c5bdb75bdef8c217 100644 (file)
@@ -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
index 7f4839a12fdfad93f09d5803b6232d95b43f8e90..379fecbf1b1708e1f5ba3f914dff6fe09bbed548 100644 (file)
@@ -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
index 258c4dd3a6f11a516c556b3724f4ed226d2958b2..adeaa9e27057e038dd66dafd88e2278913e6bf24 100644 (file)
@@ -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);