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()
This commit is contained in:
parent
659812b451
commit
2f92e2a590
@ -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`.)
|
||||
|
73
Include/internal/pycore_pymath.h
Normal file
73
Include/internal/pycore_pymath.h
Normal file
@ -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 */
|
@ -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 */
|
||||
|
@ -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
|
||||
|
@ -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.
|
@ -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");
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user