Skip to content

Commit b08a53a

Browse files
committed
Issue #1580: use short float repr where possible.
- incorporate and adapt David Gay's dtoa and strtod into the Python core - on platforms where we can use Gay's code (almost all!), repr(float) is based on the shortest sequence of decimal digits that rounds correctly. - add sys.float_repr_style attribute to indicate whether we're using Gay's code or not - add autoconf magic to detect and enable SSE2 instructions on x86/gcc - slight change to repr and str: repr switches to exponential notation at 1e16 instead of 1e17, str switches at 1e11 instead of 1e12
1 parent 579b65c commit b08a53a

File tree

19 files changed

+3866
-19
lines changed

19 files changed

+3866
-19
lines changed

Doc/library/sys.rst

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,19 @@ always available.
266266
The information in the table is simplified.
267267

268268

269+
.. data:: float_repr_style
270+
271+
A string indicating how the :func:`repr` function behaves for
272+
floats. If the string has value ``'short'`` then for a finite
273+
float ``x``, ``repr(x)`` aims to produce a short string with the
274+
property that ``float(repr(x)) == x``. This is the usual behaviour
275+
in Python 3.1 and later. Otherwise, ``float_repr_style`` has value
276+
``'legacy'`` and ``repr(x)`` behaves in the same way as it did in
277+
versions of Python prior to 3.1.
278+
279+
.. versionadded:: 3.1
280+
281+
269282
.. function:: getcheckinterval()
270283

271284
Return the interpreter's "check interval"; see :func:`setcheckinterval`.

Doc/license.rst

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -657,3 +657,35 @@ The :mod:`select` and contains the following notice for the kqueue interface::
657657
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
658658
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
659659
SUCH DAMAGE.
660+
661+
662+
strtod and dtoa
663+
---------------
664+
665+
The file :file:`Python/dtoa.c`, which supplies C functions dtoa and
666+
strtod for conversion of C doubles to and from strings, is derived
667+
from the file of the same name by David M. Gay, currently available
668+
from http://www.netlib.org/fp/. The original file, as retrieved on
669+
March 16, 2009, contains the following copyright and licensing
670+
notice::
671+
672+
/****************************************************************
673+
*
674+
* The author of this software is David M. Gay.
675+
*
676+
* Copyright (c) 1991, 2000, 2001 by Lucent Technologies.
677+
*
678+
* Permission to use, copy, modify, and distribute this software for any
679+
* purpose without fee is hereby granted, provided that this entire notice
680+
* is included in all copies of any software which is or includes a copy
681+
* or modification of this software and in all copies of the supporting
682+
* documentation for such software.
683+
*
684+
* THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
685+
* WARRANTY. IN PARTICULAR, NEITHER THE AUTHOR NOR LUCENT MAKES ANY
686+
* REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
687+
* OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
688+
*
689+
***************************************************************/
690+
691+

Include/Python.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@
118118

119119
#include "pystrtod.h"
120120
#include "pystrcmp.h"
121+
#include "dtoa.h"
121122

122123
/* _Py_Mangle is defined in compile.c */
123124
PyAPI_FUNC(PyObject*) _Py_Mangle(PyObject *p, PyObject *name);

Include/dtoa.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#ifndef PY_NO_SHORT_FLOAT_REPR
2+
#ifdef __cplusplus
3+
extern "C" {
4+
#endif
5+
6+
PyAPI_FUNC(double) _Py_dg_strtod(const char *str, char **ptr);
7+
PyAPI_FUNC(char *) _Py_dg_dtoa(double d, int mode, int ndigits,
8+
int *decpt, int *sign, char **rve);
9+
PyAPI_FUNC(void) _Py_dg_freedtoa(char *s);
10+
11+
12+
#ifdef __cplusplus
13+
}
14+
#endif
15+
#endif

Include/pymacconfig.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717
# undef SIZEOF_VOID_P
1818
# undef SIZEOF__BOOL
1919
# undef WORDS_BIGENDIAN
20+
# undef DOUBLE_IS_ARM_MIXED_ENDIAN_IEEE754
21+
# undef DOUBLE_IS_BIG_ENDIAN_IEEE754
22+
# undef DOUBLE_IS_LITTLE_ENDIAN_IEEE754
2023

2124
# undef VA_LIST_IS_ARRAY
2225
# if defined(__LP64__) && defined(__x86_64__)
@@ -65,6 +68,9 @@
6568

6669
#ifdef __BIG_ENDIAN__
6770
#define WORDS_BIGENDIAN 1
71+
#define DOUBLE_IS_BIG_ENDIAN_IEEE754
72+
#else
73+
#define DOUBLE_IS_LITTLE_ENDIAN_IEEE754
6874
#endif /* __BIG_ENDIAN */
6975

7076

Include/pymath.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,11 @@ PyAPI_FUNC(double) _Py_force_double(double);
9292
# endif
9393
#endif
9494

95+
#ifdef HAVE_GCC_ASM_FOR_X87
96+
PyAPI_FUNC(unsigned short) _Py_get_387controlword(void);
97+
PyAPI_FUNC(void) _Py_set_387controlword(unsigned short);
98+
#endif
99+
95100
/* Py_IS_NAN(X)
96101
* Return 1 if float or double arg is a NaN, else 0.
97102
* Caution:

Include/pyport.h

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,53 @@ extern "C" {
465465
errno = 0; \
466466
} while(0)
467467

468+
/* The functions _Py_dg_strtod and _Py_dg_dtoa in Python/dtoa.c require that
469+
the FPU is using 53-bit precision. Here are macros that force this. See
470+
Python/pystrtod.c for an example of their use. */
471+
472+
#ifdef USING_X87_FPU
473+
#define _Py_SET_53BIT_PRECISION_HEADER \
474+
unsigned short old_387controlword, new_387controlword
475+
#define _Py_SET_53BIT_PRECISION_START \
476+
do { \
477+
old_387controlword = _Py_get_387controlword(); \
478+
new_387controlword = (old_387controlword & ~0x0f00) | 0x0200; \
479+
if (new_387controlword != old_387controlword) \
480+
_Py_set_387controlword(new_387controlword); \
481+
} while (0)
482+
#define _Py_SET_53BIT_PRECISION_END \
483+
if (new_387controlword != old_387controlword) \
484+
_Py_set_387controlword(old_387controlword)
485+
#else
486+
#define _Py_SET_53BIT_PRECISION_HEADER
487+
#define _Py_SET_53BIT_PRECISION_START
488+
#define _Py_SET_53BIT_PRECISION_END
489+
#endif
490+
491+
/* If we can't guarantee 53-bit precision, don't use the code
492+
in Python/dtoa.c, but fall back to standard code. This
493+
means that repr of a float will be long (17 sig digits).
494+
495+
Realistically, there are two things that could go wrong:
496+
497+
(1) doubles aren't IEEE 754 doubles, or
498+
(2) we're on x86 with the rounding precision set to 64-bits
499+
(extended precision), and we don't know how to change
500+
the rounding precision.
501+
*/
502+
503+
#if !defined(DOUBLE_IS_LITTLE_ENDIAN_IEEE754) && \
504+
!defined(DOUBLE_IS_BIG_ENDIAN_IEEE754) && \
505+
!defined(DOUBLE_IS_ARM_MIXED_ENDIAN_IEEE754)
506+
#define PY_NO_SHORT_FLOAT_REPR
507+
#endif
508+
509+
/* double rounding is symptomatic of use of extended precision on x86 */
510+
#ifdef X87_DOUBLE_ROUNDING
511+
#define PY_NO_SHORT_FLOAT_REPR
512+
#endif
513+
514+
468515
/* Py_DEPRECATED(version)
469516
* Declare a variable, type, or function deprecated.
470517
* Usage:

0 commit comments

Comments
 (0)