Skip to content

Commit 9bbdde2

Browse files
authored
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.
1 parent 375a56b commit 9bbdde2

File tree

11 files changed

+104
-96
lines changed

11 files changed

+104
-96
lines changed

Doc/whatsnew/3.11.rst

+4
Original file line numberDiff line numberDiff line change
@@ -1019,3 +1019,7 @@ Removed
10191019
public C API by mistake, it must only be used by Python internally.
10201020
Use the ``PyTypeObject.tp_members`` member instead.
10211021
(Contributed by Victor Stinner in :issue:`40170`.)
1022+
1023+
* Remove the ``HAVE_PY_SET_53BIT_PRECISION`` macro (moved to the internal C
1024+
API).
1025+
(Contributed by Victor Stinner in :issue:`45412`.)

Include/internal/pycore_dtoa.h

+7-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
#ifndef PY_NO_SHORT_FLOAT_REPR
21
#ifdef __cplusplus
32
extern "C" {
43
#endif
@@ -7,6 +6,11 @@ extern "C" {
76
# error "this header requires Py_BUILD_CORE define"
87
#endif
98

9+
#include "pycore_pymath.h" // _PY_SHORT_FLOAT_REPR
10+
11+
12+
#if _PY_SHORT_FLOAT_REPR == 1
13+
1014
/* These functions are used by modules compiled as C extension like math:
1115
they must be exported. */
1216

@@ -17,7 +21,8 @@ PyAPI_FUNC(void) _Py_dg_freedtoa(char *s);
1721
PyAPI_FUNC(double) _Py_dg_stdnan(int sign);
1822
PyAPI_FUNC(double) _Py_dg_infinity(int sign);
1923

24+
#endif // _PY_SHORT_FLOAT_REPR == 1
25+
2026
#ifdef __cplusplus
2127
}
2228
#endif
23-
#endif /* !PY_NO_SHORT_FLOAT_REPR */

Include/internal/pycore_pymath.h

+57-8
Original file line numberDiff line numberDiff line change
@@ -85,19 +85,34 @@ static inline void _Py_ADJUST_ERANGE2(double x, double y)
8585
(_Py_IntegralTypeMin(type) <= v && v <= _Py_IntegralTypeMax(type))
8686

8787

88-
//--- Implementation of the HAVE_PY_SET_53BIT_PRECISION macro -------------
89-
//--- defined in pyport.h -------------------------------------------------
88+
//--- HAVE_PY_SET_53BIT_PRECISION macro ------------------------------------
9089
//
91-
// Give appropriate definitions for the following three macros:
90+
// The functions _Py_dg_strtod() and _Py_dg_dtoa() in Python/dtoa.c (which are
91+
// required to support the short float repr introduced in Python 3.1) require
92+
// that the floating-point unit that's being used for arithmetic operations on
93+
// C doubles is set to use 53-bit precision. It also requires that the FPU
94+
// rounding mode is round-half-to-even, but that's less often an issue.
9295
//
93-
// _Py_SET_53BIT_PRECISION_HEADER : any variable declarations needed to
94-
// use the two macros below.
95-
// _Py_SET_53BIT_PRECISION_START : store original FPU settings, and
96-
// set FPU to 53-bit precision/round-half-to-even
97-
// _Py_SET_53BIT_PRECISION_END : restore original FPU settings
96+
// If your FPU isn't already set to 53-bit precision/round-half-to-even, and
97+
// you want to make use of _Py_dg_strtod() and _Py_dg_dtoa(), then you should:
98+
//
99+
// #define HAVE_PY_SET_53BIT_PRECISION 1
100+
//
101+
// and also give appropriate definitions for the following three macros:
102+
//
103+
// * _Py_SET_53BIT_PRECISION_HEADER: any variable declarations needed to
104+
// use the two macros below.
105+
// * _Py_SET_53BIT_PRECISION_START: store original FPU settings, and
106+
// set FPU to 53-bit precision/round-half-to-even
107+
// * _Py_SET_53BIT_PRECISION_END: restore original FPU settings
108+
//
109+
// The macros are designed to be used within a single C function: see
110+
// Python/pystrtod.c for an example of their use.
111+
98112

99113
// Get and set x87 control word for gcc/x86
100114
#ifdef HAVE_GCC_ASM_FOR_X87
115+
#define HAVE_PY_SET_53BIT_PRECISION 1
101116

102117
// Functions defined in Python/pymath.c
103118
extern unsigned short _Py_get_387controlword(void);
@@ -124,6 +139,7 @@ extern void _Py_set_387controlword(unsigned short);
124139
// Get and set x87 control word for VisualStudio/x86.
125140
// x87 is not supported in 64-bit or ARM.
126141
#if defined(_MSC_VER) && !defined(_WIN64) && !defined(_M_ARM)
142+
#define HAVE_PY_SET_53BIT_PRECISION 1
127143

128144
#include <float.h> // __control87_2()
129145

@@ -150,7 +166,10 @@ extern void _Py_set_387controlword(unsigned short);
150166
} while (0)
151167
#endif
152168

169+
170+
// MC68881
153171
#ifdef HAVE_GCC_ASM_FOR_MC68881
172+
#define HAVE_PY_SET_53BIT_PRECISION 1
154173
#define _Py_SET_53BIT_PRECISION_HEADER \
155174
unsigned int old_fpcr, new_fpcr
156175
#define _Py_SET_53BIT_PRECISION_START \
@@ -178,6 +197,36 @@ extern void _Py_set_387controlword(unsigned short);
178197
#endif
179198

180199

200+
//--- _PY_SHORT_FLOAT_REPR macro -------------------------------------------
201+
202+
// If we can't guarantee 53-bit precision, don't use the code
203+
// in Python/dtoa.c, but fall back to standard code. This
204+
// means that repr of a float will be long (17 significant digits).
205+
//
206+
// Realistically, there are two things that could go wrong:
207+
//
208+
// (1) doubles aren't IEEE 754 doubles, or
209+
// (2) we're on x86 with the rounding precision set to 64-bits
210+
// (extended precision), and we don't know how to change
211+
// the rounding precision.
212+
#if !defined(DOUBLE_IS_LITTLE_ENDIAN_IEEE754) && \
213+
!defined(DOUBLE_IS_BIG_ENDIAN_IEEE754) && \
214+
!defined(DOUBLE_IS_ARM_MIXED_ENDIAN_IEEE754)
215+
# define _PY_SHORT_FLOAT_REPR 0
216+
#endif
217+
218+
// Double rounding is symptomatic of use of extended precision on x86.
219+
// If we're seeing double rounding, and we don't have any mechanism available
220+
// for changing the FPU rounding precision, then don't use Python/dtoa.c.
221+
#if defined(X87_DOUBLE_ROUNDING) && !defined(HAVE_PY_SET_53BIT_PRECISION)
222+
# define _PY_SHORT_FLOAT_REPR 0
223+
#endif
224+
225+
#ifndef _PY_SHORT_FLOAT_REPR
226+
# define _PY_SHORT_FLOAT_REPR 1
227+
#endif
228+
229+
181230
#ifdef __cplusplus
182231
}
183232
#endif

Include/pyport.h

-55
Original file line numberDiff line numberDiff line change
@@ -312,61 +312,6 @@ extern "C" {
312312
#define Py_SAFE_DOWNCAST(VALUE, WIDE, NARROW) (NARROW)(VALUE)
313313
#endif
314314

315-
/* The functions _Py_dg_strtod and _Py_dg_dtoa in Python/dtoa.c (which are
316-
* required to support the short float repr introduced in Python 3.1) require
317-
* that the floating-point unit that's being used for arithmetic operations
318-
* on C doubles is set to use 53-bit precision. It also requires that the
319-
* FPU rounding mode is round-half-to-even, but that's less often an issue.
320-
*
321-
* If your FPU isn't already set to 53-bit precision/round-half-to-even, and
322-
* you want to make use of _Py_dg_strtod and _Py_dg_dtoa, then you should
323-
*
324-
* #define HAVE_PY_SET_53BIT_PRECISION 1
325-
*
326-
* The macros are designed to be used within a single C function: see
327-
* Python/pystrtod.c for an example of their use.
328-
*/
329-
330-
// HAVE_PY_SET_53BIT_PRECISION macro must be kept in sync with pycore_pymath.h
331-
#ifdef HAVE_GCC_ASM_FOR_X87
332-
// Get and set x87 control word for gcc/x86
333-
# define HAVE_PY_SET_53BIT_PRECISION 1
334-
#endif
335-
#if defined(_MSC_VER) && !defined(_WIN64) && !defined(_M_ARM)
336-
// Get and set x87 control word for VisualStudio/x86.
337-
// x87 not supported in 64-bit or ARM.
338-
# define HAVE_PY_SET_53BIT_PRECISION 1
339-
#endif
340-
#ifdef HAVE_GCC_ASM_FOR_MC68881
341-
# define HAVE_PY_SET_53BIT_PRECISION 1
342-
#endif
343-
344-
345-
/* If we can't guarantee 53-bit precision, don't use the code
346-
in Python/dtoa.c, but fall back to standard code. This
347-
means that repr of a float will be long (17 sig digits).
348-
349-
Realistically, there are two things that could go wrong:
350-
351-
(1) doubles aren't IEEE 754 doubles, or
352-
(2) we're on x86 with the rounding precision set to 64-bits
353-
(extended precision), and we don't know how to change
354-
the rounding precision.
355-
*/
356-
357-
#if !defined(DOUBLE_IS_LITTLE_ENDIAN_IEEE754) && \
358-
!defined(DOUBLE_IS_BIG_ENDIAN_IEEE754) && \
359-
!defined(DOUBLE_IS_ARM_MIXED_ENDIAN_IEEE754)
360-
# define PY_NO_SHORT_FLOAT_REPR
361-
#endif
362-
363-
/* double rounding is symptomatic of use of extended precision on x86. If
364-
we're seeing double rounding, and we don't have any mechanism available for
365-
changing the FPU rounding precision, then don't use Python/dtoa.c. */
366-
#if defined(X87_DOUBLE_ROUNDING) && !defined(HAVE_PY_SET_53BIT_PRECISION)
367-
# define PY_NO_SHORT_FLOAT_REPR
368-
#endif
369-
370315

371316
/* Py_DEPRECATED(version)
372317
* Declare a variable, type, or function deprecated.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Remove the ``HAVE_PY_SET_53BIT_PRECISION`` macro (moved to the internal C API).
2+
Patch by Victor Stinner.

Modules/cmathmodule.c

+8-7
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77
#endif
88

99
#include "Python.h"
10-
#include "pycore_dtoa.h"
10+
#include "pycore_pymath.h" // _PY_SHORT_FLOAT_REPR
11+
#include "pycore_dtoa.h" // _Py_dg_stdnan()
1112
/* we need DBL_MAX, DBL_MIN, DBL_EPSILON, DBL_MANT_DIG and FLT_RADIX from
1213
float.h. We assume that FLT_RADIX is either 2 or 16. */
1314
#include <float.h>
@@ -89,14 +90,14 @@ else {
8990

9091
/* Constants cmath.inf, cmath.infj, cmath.nan, cmath.nanj.
9192
cmath.nan and cmath.nanj are defined only when either
92-
PY_NO_SHORT_FLOAT_REPR is *not* defined (which should be
93+
_PY_SHORT_FLOAT_REPR is 1 (which should be
9394
the most common situation on machines using an IEEE 754
9495
representation), or Py_NAN is defined. */
9596

9697
static double
9798
m_inf(void)
9899
{
99-
#ifndef PY_NO_SHORT_FLOAT_REPR
100+
#if _PY_SHORT_FLOAT_REPR == 1
100101
return _Py_dg_infinity(0);
101102
#else
102103
return Py_HUGE_VAL;
@@ -112,12 +113,12 @@ c_infj(void)
112113
return r;
113114
}
114115

115-
#if !defined(PY_NO_SHORT_FLOAT_REPR) || defined(Py_NAN)
116+
#if _PY_SHORT_FLOAT_REPR == 1 || defined(Py_NAN)
116117

117118
static double
118119
m_nan(void)
119120
{
120-
#ifndef PY_NO_SHORT_FLOAT_REPR
121+
#if _PY_SHORT_FLOAT_REPR == 1
121122
return _Py_dg_stdnan(0);
122123
#else
123124
return Py_NAN;
@@ -1281,7 +1282,7 @@ cmath_exec(PyObject *mod)
12811282
PyComplex_FromCComplex(c_infj())) < 0) {
12821283
return -1;
12831284
}
1284-
#if !defined(PY_NO_SHORT_FLOAT_REPR) || defined(Py_NAN)
1285+
#if _PY_SHORT_FLOAT_REPR == 1 || defined(Py_NAN)
12851286
if (PyModule_AddObject(mod, "nan", PyFloat_FromDouble(m_nan())) < 0) {
12861287
return -1;
12871288
}
@@ -1426,4 +1427,4 @@ PyMODINIT_FUNC
14261427
PyInit_cmath(void)
14271428
{
14281429
return PyModuleDef_Init(&cmathmodule);
1429-
}
1430+
}

Modules/mathmodule.c

+5-4
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ raised for division by zero and mod by zero.
6262
#include "pycore_call.h" // _PyObject_CallNoArgs()
6363
#include "pycore_dtoa.h" // _Py_dg_infinity()
6464
#include "pycore_long.h" // _PyLong_GetZero()
65+
#include "pycore_pymath.h" // _PY_SHORT_FLOAT_REPR
6566
/* For DBL_EPSILON in _math.h */
6667
#include <float.h>
6768
/* For _Py_log1p with workarounds for buggy handling of zeros. */
@@ -272,7 +273,7 @@ lanczos_sum(double x)
272273
static double
273274
m_inf(void)
274275
{
275-
#ifndef PY_NO_SHORT_FLOAT_REPR
276+
#if _PY_SHORT_FLOAT_REPR == 1
276277
return _Py_dg_infinity(0);
277278
#else
278279
return Py_HUGE_VAL;
@@ -282,12 +283,12 @@ m_inf(void)
282283
/* Constant nan value, generated in the same way as float('nan'). */
283284
/* We don't currently assume that Py_NAN is defined everywhere. */
284285

285-
#if !defined(PY_NO_SHORT_FLOAT_REPR) || defined(Py_NAN)
286+
#if _PY_SHORT_FLOAT_REPR == 1 || defined(Py_NAN)
286287

287288
static double
288289
m_nan(void)
289290
{
290-
#ifndef PY_NO_SHORT_FLOAT_REPR
291+
#if _PY_SHORT_FLOAT_REPR == 1
291292
return _Py_dg_stdnan(0);
292293
#else
293294
return Py_NAN;
@@ -3837,7 +3838,7 @@ math_exec(PyObject *module)
38373838
if (PyModule_AddObject(module, "inf", PyFloat_FromDouble(m_inf())) < 0) {
38383839
return -1;
38393840
}
3840-
#if !defined(PY_NO_SHORT_FLOAT_REPR) || defined(Py_NAN)
3841+
#if _PY_SHORT_FLOAT_REPR == 1 || defined(Py_NAN)
38413842
if (PyModule_AddObject(module, "nan", PyFloat_FromDouble(m_nan())) < 0) {
38423843
return -1;
38433844
}

Objects/floatobject.c

+8-8
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
#include "pycore_interp.h" // _PyInterpreterState.float_state
1111
#include "pycore_long.h" // _PyLong_GetOne()
1212
#include "pycore_object.h" // _PyObject_Init()
13-
#include "pycore_pymath.h" // _Py_ADJUST_ERANGE1()
13+
#include "pycore_pymath.h" // _PY_SHORT_FLOAT_REPR
1414
#include "pycore_pystate.h" // _PyInterpreterState_GET()
1515
#include "pycore_structseq.h" // _PyStructSequence_FiniType()
1616

@@ -932,7 +932,7 @@ float___ceil___impl(PyObject *self)
932932
ndigits <= 323). Returns a Python float, or sets a Python error and
933933
returns NULL on failure (OverflowError and memory errors are possible). */
934934

935-
#ifndef PY_NO_SHORT_FLOAT_REPR
935+
#if _PY_SHORT_FLOAT_REPR == 1
936936
/* version of double_round that uses the correctly-rounded string<->double
937937
conversions from Python/dtoa.c */
938938

@@ -989,7 +989,7 @@ double_round(double x, int ndigits) {
989989
return result;
990990
}
991991

992-
#else /* PY_NO_SHORT_FLOAT_REPR */
992+
#else // _PY_SHORT_FLOAT_REPR == 0
993993

994994
/* fallback version, to be used when correctly rounded binary<->decimal
995995
conversions aren't available */
@@ -1039,7 +1039,7 @@ double_round(double x, int ndigits) {
10391039
return PyFloat_FromDouble(z);
10401040
}
10411041

1042-
#endif /* PY_NO_SHORT_FLOAT_REPR */
1042+
#endif // _PY_SHORT_FLOAT_REPR == 0
10431043

10441044
/* round a Python float v to the closest multiple of 10**-ndigits */
10451045

@@ -2479,7 +2479,7 @@ _PyFloat_Unpack2(const unsigned char *p, int le)
24792479
f |= *p;
24802480

24812481
if (e == 0x1f) {
2482-
#ifdef PY_NO_SHORT_FLOAT_REPR
2482+
#if _PY_SHORT_FLOAT_REPR == 0
24832483
if (f == 0) {
24842484
/* Infinity */
24852485
return sign ? -Py_HUGE_VAL : Py_HUGE_VAL;
@@ -2494,9 +2494,9 @@ _PyFloat_Unpack2(const unsigned char *p, int le)
24942494
"can't unpack IEEE 754 NaN "
24952495
"on platform that does not support NaNs");
24962496
return -1;
2497-
#endif /* #ifdef Py_NAN */
2497+
#endif // !defined(Py_NAN)
24982498
}
2499-
#else
2499+
#else // _PY_SHORT_FLOAT_REPR == 1
25002500
if (f == 0) {
25012501
/* Infinity */
25022502
return _Py_dg_infinity(sign);
@@ -2505,7 +2505,7 @@ _PyFloat_Unpack2(const unsigned char *p, int le)
25052505
/* NaN */
25062506
return _Py_dg_stdnan(sign);
25072507
}
2508-
#endif /* #ifdef PY_NO_SHORT_FLOAT_REPR */
2508+
#endif // _PY_SHORT_FLOAT_REPR == 1
25092509
}
25102510

25112511
x = (double)f / 1024.0;

Python/dtoa.c

+4-4
Original file line numberDiff line numberDiff line change
@@ -118,12 +118,12 @@
118118
/* Linking of Python's #defines to Gay's #defines starts here. */
119119

120120
#include "Python.h"
121-
#include "pycore_dtoa.h"
121+
#include "pycore_dtoa.h" // _PY_SHORT_FLOAT_REPR
122122
#include <stdlib.h> // exit()
123123

124-
/* if PY_NO_SHORT_FLOAT_REPR is defined, then don't even try to compile
124+
/* if _PY_SHORT_FLOAT_REPR == 0, then don't even try to compile
125125
the following code */
126-
#ifndef PY_NO_SHORT_FLOAT_REPR
126+
#if _PY_SHORT_FLOAT_REPR == 1
127127

128128
#include "float.h"
129129

@@ -2857,4 +2857,4 @@ _Py_dg_dtoa(double dd, int mode, int ndigits,
28572857
}
28582858
#endif
28592859

2860-
#endif /* PY_NO_SHORT_FLOAT_REPR */
2860+
#endif // _PY_SHORT_FLOAT_REPR == 1

0 commit comments

Comments
 (0)