[Python-checkins] bpo-45412: Remove Py_SET_ERRNO_ON_MATH_ERROR() macro (GH-28820)

vstinner webhook-mailer at python.org
Mon Oct 11 15:00:30 EDT 2021


https://github.com/python/cpython/commit/2f92e2a590f0e5d2d3093549f5af9a4a1889eb5a
commit: 2f92e2a590f0e5d2d3093549f5af9a4a1889eb5a
branch: main
author: Victor Stinner <vstinner at python.org>
committer: vstinner <vstinner at python.org>
date: 2021-10-11T21:00:25+02:00
summary:

bpo-45412: Remove Py_SET_ERRNO_ON_MATH_ERROR() macro (GH-28820)

Remove the following math macros using the errno variable:

* Py_ADJUST_ERANGE1()
* Py_ADJUST_ERANGE2()
* Py_OVERFLOWED()
* Py_SET_ERANGE_IF_OVERFLOW()
* Py_SET_ERRNO_ON_MATH_ERROR()

Create pycore_pymath.h internal header file.

Rename Py_ADJUST_ERANGE1() and Py_ADJUST_ERANGE2() to
_Py_ADJUST_ERANGE1() and _Py_ADJUST_ERANGE2(), and convert these
macros to static inline functions.

Move the following macros to pycore_pymath.h:

* _Py_IntegralTypeSigned()
* _Py_IntegralTypeMax()
* _Py_IntegralTypeMin()
* _Py_InIntegralTypeRange()

files:
A Include/internal/pycore_pymath.h
A Misc/NEWS.d/next/C API/2021-10-08-15-54-07.bpo-45412.KHyJCT.rst
M Doc/whatsnew/3.11.rst
M Include/pymath.h
M Include/pyport.h
M Objects/complexobject.c
M Objects/floatobject.c
M Python/pytime.c

diff --git a/Doc/whatsnew/3.11.rst b/Doc/whatsnew/3.11.rst
index e3650a6da8b95..2262d42a99add 100644
--- a/Doc/whatsnew/3.11.rst
+++ b/Doc/whatsnew/3.11.rst
@@ -577,3 +577,13 @@ Removed
   Use the new :c:type:`PyConfig` API of the :ref:`Python Initialization Configuration
   <init-config>` instead (:pep:`587`).
   (Contributed by Victor Stinner in :issue:`44113`.)
+
+* Remove the following math macros using the ``errno`` variable:
+
+  * ``Py_ADJUST_ERANGE1()``
+  * ``Py_ADJUST_ERANGE2()``
+  * ``Py_OVERFLOWED()``
+  * ``Py_SET_ERANGE_IF_OVERFLOW()``
+  * ``Py_SET_ERRNO_ON_MATH_ERROR()``
+
+  (Contributed by Victor Stinner in :issue:`45412`.)
diff --git a/Include/internal/pycore_pymath.h b/Include/internal/pycore_pymath.h
new file mode 100644
index 0000000000000..c299c64280183
--- /dev/null
+++ b/Include/internal/pycore_pymath.h
@@ -0,0 +1,73 @@
+#ifndef Py_INTERNAL_PYMATH_H
+#define Py_INTERNAL_PYMATH_H
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef Py_BUILD_CORE
+#  error "this header requires Py_BUILD_CORE define"
+#endif
+
+/* _Py_ADJUST_ERANGE1(x)
+ * _Py_ADJUST_ERANGE2(x, y)
+ * Set errno to 0 before calling a libm function, and invoke one of these
+ * macros after, passing the function result(s) (_Py_ADJUST_ERANGE2 is useful
+ * for functions returning complex results).  This makes two kinds of
+ * adjustments to errno:  (A) If it looks like the platform libm set
+ * errno=ERANGE due to underflow, clear errno. (B) If it looks like the
+ * platform libm overflowed but didn't set errno, force errno to ERANGE.  In
+ * effect, we're trying to force a useful implementation of C89 errno
+ * behavior.
+ * Caution:
+ *    This isn't reliable.  See Py_OVERFLOWED comments.
+ *    X and Y may be evaluated more than once.
+ */
+static inline void _Py_ADJUST_ERANGE1(double x)
+{
+    if (errno == 0) {
+        if (x == Py_HUGE_VAL || x == -Py_HUGE_VAL) {
+            errno = ERANGE;
+        }
+    }
+    else if (errno == ERANGE && x == 0.0) {
+        errno = 0;
+    }
+}
+
+static inline void _Py_ADJUST_ERANGE2(double x, double y)
+{
+    if (x == Py_HUGE_VAL || x == -Py_HUGE_VAL ||
+        y == Py_HUGE_VAL || y == -Py_HUGE_VAL)
+    {
+        if (errno == 0) {
+            errno = ERANGE;
+        }
+    }
+    else if (errno == ERANGE) {
+        errno = 0;
+    }
+}
+
+// Return whether integral type *type* is signed or not.
+#define _Py_IntegralTypeSigned(type) \
+    ((type)(-1) < 0)
+
+// Return the maximum value of integral type *type*.
+#define _Py_IntegralTypeMax(type) \
+    ((_Py_IntegralTypeSigned(type)) ? (((((type)1 << (sizeof(type)*CHAR_BIT - 2)) - 1) << 1) + 1) : ~(type)0)
+
+// Return the minimum value of integral type *type*.
+#define _Py_IntegralTypeMin(type) \
+    ((_Py_IntegralTypeSigned(type)) ? -_Py_IntegralTypeMax(type) - 1 : 0)
+
+// Check whether *v* is in the range of integral type *type*. This is most
+// useful if *v* is floating-point, since demoting a floating-point *v* to an
+// integral type that cannot represent *v*'s integral part is undefined
+// behavior.
+#define _Py_InIntegralTypeRange(type, v) \
+    (_Py_IntegralTypeMin(type) <= v && v <= _Py_IntegralTypeMax(type))
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* !Py_INTERNAL_PYMATH_H */
diff --git a/Include/pymath.h b/Include/pymath.h
index ebb3b05f1b53c..39a0bdad98b9e 100644
--- a/Include/pymath.h
+++ b/Include/pymath.h
@@ -176,50 +176,4 @@ PyAPI_FUNC(void) _Py_set_387controlword(unsigned short);
 #endif /* __INTEL_COMPILER */
 #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
- * result.
- * Caution:
- *    This isn't reliable.  C99 no longer requires libm to set errno under
- *        any exceptional condition, but does require +- HUGE_VAL return
- *        values on overflow.  A 754 box *probably* maps HUGE_VAL to a
- *        double infinity, and we're cool if that's so, unless the input
- *        was an infinity and an infinity is the expected result.  A C89
- *        system sets errno to ERANGE, so we check for that too.  We're
- *        out of luck if a C99 754 box doesn't map HUGE_VAL to +Inf, or
- *        if the returned result is a NaN, or if a C89 box returns HUGE_VAL
- *        in non-overflow cases.
- *    X is evaluated more than once.
- * Some platforms have better way to spell this, so expect some #ifdef'ery.
- *
- * OpenBSD uses 'isinf()' because a compiler bug on that platform causes
- * the longer macro version to be mis-compiled. This isn't optimal, and
- * should be removed once a newer compiler is available on that platform.
- * The system that had the failure was running OpenBSD 3.2 on Intel, with
- * gcc 2.95.3.
- *
- * According to Tim's checkin, the FreeBSD systems use isinf() to work
- * around a FPE bug on that platform.
- */
-#if defined(__FreeBSD__) || defined(__OpenBSD__)
-#define Py_OVERFLOWED(X) isinf(X)
-#else
-#define Py_OVERFLOWED(X) ((X) != 0.0 && (errno == ERANGE ||    \
-                                         (X) == Py_HUGE_VAL || \
-                                         (X) == -Py_HUGE_VAL))
-#endif
-
-/* Return whether integral type *type* is signed or not. */
-#define _Py_IntegralTypeSigned(type) ((type)(-1) < 0)
-/* Return the maximum value of integral type *type*. */
-#define _Py_IntegralTypeMax(type) ((_Py_IntegralTypeSigned(type)) ? (((((type)1 << (sizeof(type)*CHAR_BIT - 2)) - 1) << 1) + 1) : ~(type)0)
-/* Return the minimum value of integral type *type*. */
-#define _Py_IntegralTypeMin(type) ((_Py_IntegralTypeSigned(type)) ? -_Py_IntegralTypeMax(type) - 1 : 0)
-/* Check whether *v* is in the range of integral type *type*. This is most
- * useful if *v* is floating-point, since demoting a floating-point *v* to an
- * integral type that cannot represent *v*'s integral part is undefined
- * behavior. */
-#define _Py_InIntegralTypeRange(type, v) (_Py_IntegralTypeMin(type) <= v && v <= _Py_IntegralTypeMax(type))
-
 #endif /* Py_PYMATH_H */
diff --git a/Include/pyport.h b/Include/pyport.h
index d27d0838f1b2c..ffc9ba55618d8 100644
--- a/Include/pyport.h
+++ b/Include/pyport.h
@@ -316,69 +316,6 @@ extern "C" {
 #define Py_SAFE_DOWNCAST(VALUE, WIDE, NARROW) (NARROW)(VALUE)
 #endif
 
-/* Py_SET_ERRNO_ON_MATH_ERROR(x)
- * If a libm function did not set errno, but it looks like the result
- * overflowed or not-a-number, set errno to ERANGE or EDOM.  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.
- */
-#if defined(__FreeBSD__) || defined(__OpenBSD__) || (defined(__hpux) && defined(__ia64))
-#define _Py_SET_EDOM_FOR_NAN(X) if (isnan(X)) errno = EDOM;
-#else
-#define _Py_SET_EDOM_FOR_NAN(X) ;
-#endif
-#define Py_SET_ERRNO_ON_MATH_ERROR(X) \
-    do { \
-        if (errno == 0) { \
-            if ((X) == Py_HUGE_VAL || (X) == -Py_HUGE_VAL) \
-                errno = ERANGE; \
-            else _Py_SET_EDOM_FOR_NAN(X) \
-        } \
-    } while(0)
-
-/* Py_SET_ERANGE_IF_OVERFLOW(x)
- * An alias of Py_SET_ERRNO_ON_MATH_ERROR for backward-compatibility.
- */
-#define Py_SET_ERANGE_IF_OVERFLOW(X) Py_SET_ERRNO_ON_MATH_ERROR(X)
-
-/* Py_ADJUST_ERANGE1(x)
- * Py_ADJUST_ERANGE2(x, y)
- * Set errno to 0 before calling a libm function, and invoke one of these
- * macros after, passing the function result(s) (Py_ADJUST_ERANGE2 is useful
- * for functions returning complex results).  This makes two kinds of
- * adjustments to errno:  (A) If it looks like the platform libm set
- * errno=ERANGE due to underflow, clear errno. (B) If it looks like the
- * platform libm overflowed but didn't set errno, force errno to ERANGE.  In
- * effect, we're trying to force a useful implementation of C89 errno
- * behavior.
- * Caution:
- *    This isn't reliable.  See Py_OVERFLOWED comments.
- *    X and Y may be evaluated more than once.
- */
-#define Py_ADJUST_ERANGE1(X)                                            \
-    do {                                                                \
-        if (errno == 0) {                                               \
-            if ((X) == Py_HUGE_VAL || (X) == -Py_HUGE_VAL)              \
-                errno = ERANGE;                                         \
-        }                                                               \
-        else if (errno == ERANGE && (X) == 0.0)                         \
-            errno = 0;                                                  \
-    } while(0)
-
-#define Py_ADJUST_ERANGE2(X, Y)                                         \
-    do {                                                                \
-        if ((X) == Py_HUGE_VAL || (X) == -Py_HUGE_VAL ||                \
-            (Y) == Py_HUGE_VAL || (Y) == -Py_HUGE_VAL) {                \
-                        if (errno == 0)                                 \
-                                errno = ERANGE;                         \
-        }                                                               \
-        else if (errno == ERANGE)                                       \
-            errno = 0;                                                  \
-    } while(0)
-
 /*  The functions _Py_dg_strtod and _Py_dg_dtoa in Python/dtoa.c (which are
  *  required to support the short float repr introduced in Python 3.1) require
  *  that the floating-point unit that's being used for arithmetic operations
diff --git a/Misc/NEWS.d/next/C API/2021-10-08-15-54-07.bpo-45412.KHyJCT.rst b/Misc/NEWS.d/next/C API/2021-10-08-15-54-07.bpo-45412.KHyJCT.rst
new file mode 100644
index 0000000000000..49746810ee5b9
--- /dev/null
+++ b/Misc/NEWS.d/next/C API/2021-10-08-15-54-07.bpo-45412.KHyJCT.rst	
@@ -0,0 +1,9 @@
+Remove the following math macros using the ``errno`` variable:
+
+* ``Py_ADJUST_ERANGE1()``
+* ``Py_ADJUST_ERANGE2()``
+* ``Py_OVERFLOWED()``
+* ``Py_SET_ERANGE_IF_OVERFLOW()``
+* ``Py_SET_ERRNO_ON_MATH_ERROR()``
+
+Patch by Victor Stinner.
diff --git a/Objects/complexobject.c b/Objects/complexobject.c
index cfe6c737578a0..f08f03fcb49a5 100644
--- a/Objects/complexobject.c
+++ b/Objects/complexobject.c
@@ -8,6 +8,7 @@
 #include "Python.h"
 #include "pycore_long.h"          // _PyLong_GetZero()
 #include "pycore_object.h"        // _PyObject_Init()
+#include "pycore_pymath.h"        // _Py_ADJUST_ERANGE2()
 #include "structmember.h"         // PyMemberDef
 
 
@@ -525,7 +526,7 @@ complex_pow(PyObject *v, PyObject *w, PyObject *z)
         p = _Py_c_pow(a, b);
     }
 
-    Py_ADJUST_ERANGE2(p.real, p.imag);
+    _Py_ADJUST_ERANGE2(p.real, p.imag);
     if (errno == EDOM) {
         PyErr_SetString(PyExc_ZeroDivisionError,
                         "0.0 to a negative or complex power");
diff --git a/Objects/floatobject.c b/Objects/floatobject.c
index e4ce7e74d2c57..d25d97f4cbf61 100644
--- a/Objects/floatobject.c
+++ b/Objects/floatobject.c
@@ -8,6 +8,7 @@
 #include "pycore_interp.h"        // _PyInterpreterState.float_state
 #include "pycore_long.h"          // _PyLong_GetOne()
 #include "pycore_object.h"        // _PyObject_Init()
+#include "pycore_pymath.h"        // _Py_ADJUST_ERANGE1()
 #include "pycore_pystate.h"       // _PyInterpreterState_GET()
 
 #include <ctype.h>
@@ -809,7 +810,7 @@ float_pow(PyObject *v, PyObject *w, PyObject *z)
      */
     errno = 0;
     ix = pow(iv, iw);
-    Py_ADJUST_ERANGE1(ix);
+    _Py_ADJUST_ERANGE1(ix);
     if (negate_result)
         ix = -ix;
 
diff --git a/Python/pytime.c b/Python/pytime.c
index 8865638e91c23..9653662b0fb14 100644
--- a/Python/pytime.c
+++ b/Python/pytime.c
@@ -1,10 +1,11 @@
 #include "Python.h"
+#include "pycore_pymath.h"        // _Py_InIntegralTypeRange()
 #ifdef MS_WINDOWS
-#include <winsock2.h>         /* struct timeval */
+#  include <winsock2.h>           // struct timeval
 #endif
 
 #if defined(__APPLE__)
-#include <mach/mach_time.h>   /* mach_absolute_time(), mach_timebase_info() */
+#  include <mach/mach_time.h>     // mach_absolute_time(), mach_timebase_info()
 
 #if defined(__APPLE__) && defined(__has_builtin)
 #  if __has_builtin(__builtin_available)



More information about the Python-checkins mailing list