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.
This commit is contained in:
Victor Stinner 2022-02-23 18:16:23 +01:00 committed by GitHub
parent 375a56bd40
commit 9bbdde2180
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 104 additions and 96 deletions

View File

@ -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`.)

View File

@ -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 */

View File

@ -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

View File

@ -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.

View File

@ -0,0 +1,2 @@
Remove the ``HAVE_PY_SET_53BIT_PRECISION`` macro (moved to the internal C API).
Patch by Victor Stinner.

View File

@ -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);
}
}

View File

@ -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;
}

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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");