[Python-checkins] bpo-45412: Add _PY_SHORT_FLOAT_REPR macro (GH-31171)

vstinner webhook-mailer at python.org
Wed Feb 23 12:16:41 EST 2022


https://github.com/python/cpython/commit/9bbdde218005f552304d9954bb97e3f9185edded
commit: 9bbdde218005f552304d9954bb97e3f9185edded
branch: main
author: Victor Stinner <vstinner at python.org>
committer: vstinner <vstinner at python.org>
date: 2022-02-23T18:16:23+01:00
summary:

bpo-45412: Add _PY_SHORT_FLOAT_REPR macro (GH-31171)

Remove the HAVE_PY_SET_53BIT_PRECISION macro (moved to the internal
C API).

* Move HAVE_PY_SET_53BIT_PRECISION macro to pycore_pymath.h.
* Replace PY_NO_SHORT_FLOAT_REPR macro with _PY_SHORT_FLOAT_REPR
  macro which is always defined. gcc -Wundef emits a warning when
  using _PY_SHORT_FLOAT_REPR but the macro is not defined, if
  pycore_pymath.h include was forgotten.

files:
A Misc/NEWS.d/next/C API/2022-02-06-20-14-21.bpo-45412.XJVaGW.rst
M Doc/whatsnew/3.11.rst
M Include/internal/pycore_dtoa.h
M Include/internal/pycore_pymath.h
M Include/pyport.h
M Modules/cmathmodule.c
M Modules/mathmodule.c
M Objects/floatobject.c
M Python/dtoa.c
M Python/pystrtod.c
M Python/sysmodule.c

diff --git a/Doc/whatsnew/3.11.rst b/Doc/whatsnew/3.11.rst
index 32f021f03ccce..34642e320dd18 100644
--- a/Doc/whatsnew/3.11.rst
+++ b/Doc/whatsnew/3.11.rst
@@ -1019,3 +1019,7 @@ Removed
   public C API by mistake, it must only be used by Python internally.
   Use the ``PyTypeObject.tp_members`` member instead.
   (Contributed by Victor Stinner in :issue:`40170`.)
+
+* Remove the ``HAVE_PY_SET_53BIT_PRECISION`` macro (moved to the internal C
+  API).
+  (Contributed by Victor Stinner in :issue:`45412`.)
diff --git a/Include/internal/pycore_dtoa.h b/Include/internal/pycore_dtoa.h
index 3faf8cf6b2eef..c77cf6e46cc3c 100644
--- a/Include/internal/pycore_dtoa.h
+++ b/Include/internal/pycore_dtoa.h
@@ -1,4 +1,3 @@
-#ifndef PY_NO_SHORT_FLOAT_REPR
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -7,6 +6,11 @@ extern "C" {
 #  error "this header requires Py_BUILD_CORE define"
 #endif
 
+#include "pycore_pymath.h"        // _PY_SHORT_FLOAT_REPR
+
+
+#if _PY_SHORT_FLOAT_REPR == 1
+
 /* These functions are used by modules compiled as C extension like math:
    they must be exported. */
 
@@ -17,7 +21,8 @@ PyAPI_FUNC(void) _Py_dg_freedtoa(char *s);
 PyAPI_FUNC(double) _Py_dg_stdnan(int sign);
 PyAPI_FUNC(double) _Py_dg_infinity(int sign);
 
+#endif // _PY_SHORT_FLOAT_REPR == 1
+
 #ifdef __cplusplus
 }
 #endif
-#endif   /* !PY_NO_SHORT_FLOAT_REPR */
diff --git a/Include/internal/pycore_pymath.h b/Include/internal/pycore_pymath.h
index 395b71452e40a..1f54b3d81325d 100644
--- a/Include/internal/pycore_pymath.h
+++ b/Include/internal/pycore_pymath.h
@@ -85,19 +85,34 @@ static inline void _Py_ADJUST_ERANGE2(double x, double y)
     (_Py_IntegralTypeMin(type) <= v && v <= _Py_IntegralTypeMax(type))
 
 
-//--- Implementation of the HAVE_PY_SET_53BIT_PRECISION macro -------------
-//--- defined in pyport.h -------------------------------------------------
+//--- HAVE_PY_SET_53BIT_PRECISION macro ------------------------------------
 //
-// Give appropriate definitions for the following three macros:
+// 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 on
+// C doubles is set to use 53-bit precision.  It also requires that the FPU
+// rounding mode is round-half-to-even, but that's less often an issue.
 //
-//    _Py_SET_53BIT_PRECISION_HEADER : any variable declarations needed to
-//        use the two macros below.
-//    _Py_SET_53BIT_PRECISION_START : store original FPU settings, and
-//        set FPU to 53-bit precision/round-half-to-even
-//    _Py_SET_53BIT_PRECISION_END : restore original FPU settings
+// If your FPU isn't already set to 53-bit precision/round-half-to-even, and
+// you want to make use of _Py_dg_strtod() and _Py_dg_dtoa(), then you should:
+//
+//     #define HAVE_PY_SET_53BIT_PRECISION 1
+//
+// and also give appropriate definitions for the following three macros:
+//
+// * _Py_SET_53BIT_PRECISION_HEADER: any variable declarations needed to
+//   use the two macros below.
+// * _Py_SET_53BIT_PRECISION_START: store original FPU settings, and
+//   set FPU to 53-bit precision/round-half-to-even
+// * _Py_SET_53BIT_PRECISION_END: restore original FPU settings
+//
+// The macros are designed to be used within a single C function: see
+// Python/pystrtod.c for an example of their use.
+
 
 // Get and set x87 control word for gcc/x86
 #ifdef HAVE_GCC_ASM_FOR_X87
+#define HAVE_PY_SET_53BIT_PRECISION 1
 
 // Functions defined in Python/pymath.c
 extern unsigned short _Py_get_387controlword(void);
@@ -124,6 +139,7 @@ extern void _Py_set_387controlword(unsigned short);
 // Get and set x87 control word for VisualStudio/x86.
 // x87 is not supported in 64-bit or ARM.
 #if defined(_MSC_VER) && !defined(_WIN64) && !defined(_M_ARM)
+#define HAVE_PY_SET_53BIT_PRECISION 1
 
 #include <float.h>                // __control87_2()
 
@@ -150,7 +166,10 @@ extern void _Py_set_387controlword(unsigned short);
     } while (0)
 #endif
 
+
+// MC68881
 #ifdef HAVE_GCC_ASM_FOR_MC68881
+#define HAVE_PY_SET_53BIT_PRECISION 1
 #define _Py_SET_53BIT_PRECISION_HEADER \
     unsigned int old_fpcr, new_fpcr
 #define _Py_SET_53BIT_PRECISION_START                                   \
@@ -178,6 +197,36 @@ extern void _Py_set_387controlword(unsigned short);
 #endif
 
 
+//--- _PY_SHORT_FLOAT_REPR macro -------------------------------------------
+
+// If we can't guarantee 53-bit precision, don't use the code
+// in Python/dtoa.c, but fall back to standard code.  This
+// means that repr of a float will be long (17 significant digits).
+//
+// Realistically, there are two things that could go wrong:
+//
+// (1) doubles aren't IEEE 754 doubles, or
+// (2) we're on x86 with the rounding precision set to 64-bits
+//     (extended precision), and we don't know how to change
+//     the rounding precision.
+#if !defined(DOUBLE_IS_LITTLE_ENDIAN_IEEE754) && \
+    !defined(DOUBLE_IS_BIG_ENDIAN_IEEE754) && \
+    !defined(DOUBLE_IS_ARM_MIXED_ENDIAN_IEEE754)
+#  define _PY_SHORT_FLOAT_REPR 0
+#endif
+
+// Double rounding is symptomatic of use of extended precision on x86.
+// If we're seeing double rounding, and we don't have any mechanism available
+// for changing the FPU rounding precision, then don't use Python/dtoa.c.
+#if defined(X87_DOUBLE_ROUNDING) && !defined(HAVE_PY_SET_53BIT_PRECISION)
+#  define _PY_SHORT_FLOAT_REPR 0
+#endif
+
+#ifndef _PY_SHORT_FLOAT_REPR
+#  define _PY_SHORT_FLOAT_REPR 1
+#endif
+
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/Include/pyport.h b/Include/pyport.h
index d27b3dde11659..62ac0989d3f15 100644
--- a/Include/pyport.h
+++ b/Include/pyport.h
@@ -312,61 +312,6 @@ extern "C" {
 #define Py_SAFE_DOWNCAST(VALUE, WIDE, NARROW) (NARROW)(VALUE)
 #endif
 
-/*  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
- *  on C doubles is set to use 53-bit precision.  It also requires that the
- *  FPU rounding mode is round-half-to-even, but that's less often an issue.
- *
- *  If your FPU isn't already set to 53-bit precision/round-half-to-even, and
- *  you want to make use of _Py_dg_strtod and _Py_dg_dtoa, then you should
- *
- *     #define HAVE_PY_SET_53BIT_PRECISION 1
- *
- * The macros are designed to be used within a single C function: see
- * Python/pystrtod.c for an example of their use.
- */
-
-// HAVE_PY_SET_53BIT_PRECISION macro must be kept in sync with pycore_pymath.h
-#ifdef HAVE_GCC_ASM_FOR_X87
-   // Get and set x87 control word for gcc/x86
-#  define HAVE_PY_SET_53BIT_PRECISION 1
-#endif
-#if defined(_MSC_VER) && !defined(_WIN64) && !defined(_M_ARM)
-   // Get and set x87 control word for VisualStudio/x86.
-   // x87 not supported in 64-bit or ARM.
-#  define HAVE_PY_SET_53BIT_PRECISION 1
-#endif
-#ifdef HAVE_GCC_ASM_FOR_MC68881
-#  define HAVE_PY_SET_53BIT_PRECISION 1
-#endif
-
-
-/* If we can't guarantee 53-bit precision, don't use the code
-   in Python/dtoa.c, but fall back to standard code.  This
-   means that repr of a float will be long (17 sig digits).
-
-   Realistically, there are two things that could go wrong:
-
-   (1) doubles aren't IEEE 754 doubles, or
-   (2) we're on x86 with the rounding precision set to 64-bits
-       (extended precision), and we don't know how to change
-       the rounding precision.
- */
-
-#if !defined(DOUBLE_IS_LITTLE_ENDIAN_IEEE754) && \
-    !defined(DOUBLE_IS_BIG_ENDIAN_IEEE754) && \
-    !defined(DOUBLE_IS_ARM_MIXED_ENDIAN_IEEE754)
-#  define PY_NO_SHORT_FLOAT_REPR
-#endif
-
-/* double rounding is symptomatic of use of extended precision on x86.  If
-   we're seeing double rounding, and we don't have any mechanism available for
-   changing the FPU rounding precision, then don't use Python/dtoa.c. */
-#if defined(X87_DOUBLE_ROUNDING) && !defined(HAVE_PY_SET_53BIT_PRECISION)
-#  define PY_NO_SHORT_FLOAT_REPR
-#endif
-
 
 /* Py_DEPRECATED(version)
  * Declare a variable, type, or function deprecated.
diff --git a/Misc/NEWS.d/next/C API/2022-02-06-20-14-21.bpo-45412.XJVaGW.rst b/Misc/NEWS.d/next/C API/2022-02-06-20-14-21.bpo-45412.XJVaGW.rst
new file mode 100644
index 0000000000000..5c0cde1f2b087
--- /dev/null
+++ b/Misc/NEWS.d/next/C API/2022-02-06-20-14-21.bpo-45412.XJVaGW.rst	
@@ -0,0 +1,2 @@
+Remove the ``HAVE_PY_SET_53BIT_PRECISION`` macro (moved to the internal C API).
+Patch by Victor Stinner.
diff --git a/Modules/cmathmodule.c b/Modules/cmathmodule.c
index 281d3937e2656..c0c0c353d196c 100644
--- a/Modules/cmathmodule.c
+++ b/Modules/cmathmodule.c
@@ -7,7 +7,8 @@
 #endif
 
 #include "Python.h"
-#include "pycore_dtoa.h"
+#include "pycore_pymath.h"        // _PY_SHORT_FLOAT_REPR
+#include "pycore_dtoa.h"          // _Py_dg_stdnan()
 /* we need DBL_MAX, DBL_MIN, DBL_EPSILON, DBL_MANT_DIG and FLT_RADIX from
    float.h.  We assume that FLT_RADIX is either 2 or 16. */
 #include <float.h>
@@ -89,14 +90,14 @@ else {
 
 /* Constants cmath.inf, cmath.infj, cmath.nan, cmath.nanj.
    cmath.nan and cmath.nanj are defined only when either
-   PY_NO_SHORT_FLOAT_REPR is *not* defined (which should be
+   _PY_SHORT_FLOAT_REPR is 1 (which should be
    the most common situation on machines using an IEEE 754
    representation), or Py_NAN is defined. */
 
 static double
 m_inf(void)
 {
-#ifndef PY_NO_SHORT_FLOAT_REPR
+#if _PY_SHORT_FLOAT_REPR == 1
     return _Py_dg_infinity(0);
 #else
     return Py_HUGE_VAL;
@@ -112,12 +113,12 @@ c_infj(void)
     return r;
 }
 
-#if !defined(PY_NO_SHORT_FLOAT_REPR) || defined(Py_NAN)
+#if _PY_SHORT_FLOAT_REPR == 1 || defined(Py_NAN)
 
 static double
 m_nan(void)
 {
-#ifndef PY_NO_SHORT_FLOAT_REPR
+#if _PY_SHORT_FLOAT_REPR == 1
     return _Py_dg_stdnan(0);
 #else
     return Py_NAN;
@@ -1281,7 +1282,7 @@ cmath_exec(PyObject *mod)
                            PyComplex_FromCComplex(c_infj())) < 0) {
         return -1;
     }
-#if !defined(PY_NO_SHORT_FLOAT_REPR) || defined(Py_NAN)
+#if _PY_SHORT_FLOAT_REPR == 1 || defined(Py_NAN)
     if (PyModule_AddObject(mod, "nan", PyFloat_FromDouble(m_nan())) < 0) {
         return -1;
     }
@@ -1426,4 +1427,4 @@ PyMODINIT_FUNC
 PyInit_cmath(void)
 {
     return PyModuleDef_Init(&cmathmodule);
-}
\ No newline at end of file
+}
diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c
index 721c9a63d51b2..24ae1460e5a89 100644
--- a/Modules/mathmodule.c
+++ b/Modules/mathmodule.c
@@ -62,6 +62,7 @@ raised for division by zero and mod by zero.
 #include "pycore_call.h"          // _PyObject_CallNoArgs()
 #include "pycore_dtoa.h"          // _Py_dg_infinity()
 #include "pycore_long.h"          // _PyLong_GetZero()
+#include "pycore_pymath.h"        // _PY_SHORT_FLOAT_REPR
 /* For DBL_EPSILON in _math.h */
 #include <float.h>
 /* For _Py_log1p with workarounds for buggy handling of zeros. */
@@ -272,7 +273,7 @@ lanczos_sum(double x)
 static double
 m_inf(void)
 {
-#ifndef PY_NO_SHORT_FLOAT_REPR
+#if _PY_SHORT_FLOAT_REPR == 1
     return _Py_dg_infinity(0);
 #else
     return Py_HUGE_VAL;
@@ -282,12 +283,12 @@ m_inf(void)
 /* Constant nan value, generated in the same way as float('nan'). */
 /* We don't currently assume that Py_NAN is defined everywhere. */
 
-#if !defined(PY_NO_SHORT_FLOAT_REPR) || defined(Py_NAN)
+#if _PY_SHORT_FLOAT_REPR == 1 || defined(Py_NAN)
 
 static double
 m_nan(void)
 {
-#ifndef PY_NO_SHORT_FLOAT_REPR
+#if _PY_SHORT_FLOAT_REPR == 1
     return _Py_dg_stdnan(0);
 #else
     return Py_NAN;
@@ -3837,7 +3838,7 @@ math_exec(PyObject *module)
     if (PyModule_AddObject(module, "inf", PyFloat_FromDouble(m_inf())) < 0) {
         return -1;
     }
-#if !defined(PY_NO_SHORT_FLOAT_REPR) || defined(Py_NAN)
+#if _PY_SHORT_FLOAT_REPR == 1 || defined(Py_NAN)
     if (PyModule_AddObject(module, "nan", PyFloat_FromDouble(m_nan())) < 0) {
         return -1;
     }
diff --git a/Objects/floatobject.c b/Objects/floatobject.c
index 79fbdabce9608..64d4c3e5a8cef 100644
--- a/Objects/floatobject.c
+++ b/Objects/floatobject.c
@@ -10,7 +10,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_pymath.h"        // _PY_SHORT_FLOAT_REPR
 #include "pycore_pystate.h"       // _PyInterpreterState_GET()
 #include "pycore_structseq.h"     // _PyStructSequence_FiniType()
 
@@ -932,7 +932,7 @@ float___ceil___impl(PyObject *self)
    ndigits <= 323).  Returns a Python float, or sets a Python error and
    returns NULL on failure (OverflowError and memory errors are possible). */
 
-#ifndef PY_NO_SHORT_FLOAT_REPR
+#if _PY_SHORT_FLOAT_REPR == 1
 /* version of double_round that uses the correctly-rounded string<->double
    conversions from Python/dtoa.c */
 
@@ -989,7 +989,7 @@ double_round(double x, int ndigits) {
     return result;
 }
 
-#else /* PY_NO_SHORT_FLOAT_REPR */
+#else  // _PY_SHORT_FLOAT_REPR == 0
 
 /* fallback version, to be used when correctly rounded binary<->decimal
    conversions aren't available */
@@ -1039,7 +1039,7 @@ double_round(double x, int ndigits) {
     return PyFloat_FromDouble(z);
 }
 
-#endif /* PY_NO_SHORT_FLOAT_REPR */
+#endif  // _PY_SHORT_FLOAT_REPR == 0
 
 /* round a Python float v to the closest multiple of 10**-ndigits */
 
@@ -2479,7 +2479,7 @@ _PyFloat_Unpack2(const unsigned char *p, int le)
     f |= *p;
 
     if (e == 0x1f) {
-#ifdef PY_NO_SHORT_FLOAT_REPR
+#if _PY_SHORT_FLOAT_REPR == 0
         if (f == 0) {
             /* Infinity */
             return sign ? -Py_HUGE_VAL : Py_HUGE_VAL;
@@ -2494,9 +2494,9 @@ _PyFloat_Unpack2(const unsigned char *p, int le)
                 "can't unpack IEEE 754 NaN "
                 "on platform that does not support NaNs");
             return -1;
-#endif  /* #ifdef Py_NAN */
+#endif  // !defined(Py_NAN)
         }
-#else
+#else  // _PY_SHORT_FLOAT_REPR == 1
         if (f == 0) {
             /* Infinity */
             return _Py_dg_infinity(sign);
@@ -2505,7 +2505,7 @@ _PyFloat_Unpack2(const unsigned char *p, int le)
             /* NaN */
             return _Py_dg_stdnan(sign);
         }
-#endif  /* #ifdef PY_NO_SHORT_FLOAT_REPR */
+#endif  // _PY_SHORT_FLOAT_REPR == 1
     }
 
     x = (double)f / 1024.0;
diff --git a/Python/dtoa.c b/Python/dtoa.c
index 6c44f68965176..733e70bc79169 100644
--- a/Python/dtoa.c
+++ b/Python/dtoa.c
@@ -118,12 +118,12 @@
 /* Linking of Python's #defines to Gay's #defines starts here. */
 
 #include "Python.h"
-#include "pycore_dtoa.h"
+#include "pycore_dtoa.h"          // _PY_SHORT_FLOAT_REPR
 #include <stdlib.h>               // exit()
 
-/* if PY_NO_SHORT_FLOAT_REPR is defined, then don't even try to compile
+/* if _PY_SHORT_FLOAT_REPR == 0, then don't even try to compile
    the following code */
-#ifndef PY_NO_SHORT_FLOAT_REPR
+#if _PY_SHORT_FLOAT_REPR == 1
 
 #include "float.h"
 
@@ -2857,4 +2857,4 @@ _Py_dg_dtoa(double dd, int mode, int ndigits,
 }
 #endif
 
-#endif  /* PY_NO_SHORT_FLOAT_REPR */
+#endif  // _PY_SHORT_FLOAT_REPR == 1
diff --git a/Python/pystrtod.c b/Python/pystrtod.c
index ab5814de21ba5..7469d6259d673 100644
--- a/Python/pystrtod.c
+++ b/Python/pystrtod.c
@@ -1,8 +1,8 @@
 /* -*- Mode: C; c-file-style: "python" -*- */
 
 #include <Python.h>
-#include "pycore_dtoa.h"
-#include "pycore_pymath.h"        // _Py_SET_53BIT_PRECISION_START
+#include "pycore_dtoa.h"          // _Py_dg_strtod()
+#include "pycore_pymath.h"        // _PY_SHORT_FLOAT_REPR
 #include <locale.h>
 
 /* Case-insensitive string match used for nan and inf detection; t should be
@@ -24,7 +24,7 @@ case_insensitive_match(const char *s, const char *t)
    the successfully parsed portion of the string.  On failure, return -1.0 and
    set *endptr to point to the start of the string. */
 
-#ifndef PY_NO_SHORT_FLOAT_REPR
+#if _PY_SHORT_FLOAT_REPR == 1
 
 double
 _Py_parse_inf_or_nan(const char *p, char **endptr)
@@ -127,7 +127,7 @@ _Py_parse_inf_or_nan(const char *p, char **endptr)
  * Return value: the #gdouble value.
  **/
 
-#ifndef PY_NO_SHORT_FLOAT_REPR
+#if _PY_SHORT_FLOAT_REPR == 1
 
 static double
 _PyOS_ascii_strtod(const char *nptr, char **endptr)
@@ -441,7 +441,7 @@ _Py_string_to_number_with_underscores(
     return NULL;
 }
 
-#ifdef PY_NO_SHORT_FLOAT_REPR
+#if _PY_SHORT_FLOAT_REPR == 0
 
 /* Given a string that may have a decimal point in the current
    locale, change it back to a dot.  Since the string cannot get
@@ -942,7 +942,7 @@ char * PyOS_double_to_string(double val,
     return buf;
 }
 
-#else
+#else  // _PY_SHORT_FLOAT_REPR == 1
 
 /* _Py_dg_dtoa is available. */
 
@@ -1305,4 +1305,4 @@ char * PyOS_double_to_string(double val,
                               flags & Py_DTSF_ALT,
                               float_strings, type);
 }
-#endif /* ifdef PY_NO_SHORT_FLOAT_REPR */
+#endif  // _PY_SHORT_FLOAT_REPR == 1
diff --git a/Python/sysmodule.c b/Python/sysmodule.c
index e23b879c1ab2b..342e48ed4222b 100644
--- a/Python/sysmodule.c
+++ b/Python/sysmodule.c
@@ -25,6 +25,7 @@ Data members:
 #include "pycore_pathconfig.h"    // _PyPathConfig_ComputeSysPath0()
 #include "pycore_pyerrors.h"      // _PyErr_Fetch()
 #include "pycore_pylifecycle.h"   // _PyErr_WriteUnraisableDefaultHook()
+#include "pycore_pymath.h"        // _PY_SHORT_FLOAT_REPR
 #include "pycore_pymem.h"         // _PyMem_SetDefaultAllocator()
 #include "pycore_pystate.h"       // _PyThreadState_GET()
 #include "pycore_structseq.h"     // _PyStructSequence_InitType()
@@ -2837,7 +2838,7 @@ _PySys_InitCore(PyThreadState *tstate, PyObject *sysdict)
 #endif
 
     /* float repr style: 0.03 (short) vs 0.029999999999999999 (legacy) */
-#ifndef PY_NO_SHORT_FLOAT_REPR
+#if _PY_SHORT_FLOAT_REPR == 1
     SET_SYS_FROM_STRING("float_repr_style", "short");
 #else
     SET_SYS_FROM_STRING("float_repr_style", "legacy");



More information about the Python-checkins mailing list