diff --git a/Doc/library/sys.rst b/Doc/library/sys.rst index 5e47201f88eae1..ff1a6f508f036a 100644 --- a/Doc/library/sys.rst +++ b/Doc/library/sys.rst @@ -607,6 +607,9 @@ always available. .. versionadded:: 3.1 + .. versionchanged:: 3.11 + The value is now always ``'short'``. + .. function:: getallocatedblocks() diff --git a/Doc/whatsnew/3.11.rst b/Doc/whatsnew/3.11.rst index 8ebe1cb8cc4ca9..6e752024cebe35 100644 --- a/Doc/whatsnew/3.11.rst +++ b/Doc/whatsnew/3.11.rst @@ -335,6 +335,9 @@ sys (equivalent to ``sys.exc_info()[1]``). (Contributed by Irit Katriel in :issue:`46328`.) +* :data:`sys.float_repr_style` is now always ``'short'``. + (Contributed by Victor Stinner in :issue:`46852`.) + threading --------- @@ -646,6 +649,9 @@ Build Changes remove the ``Py_NO_NAN`` macro. (Contributed by Victor Stinner in :issue:`46656`.) +* Building Python now requires IEEE 754 (floating point) support. + (Contributed by Victor Stinner in :issue:`46852`.) + * Freelists for object structs can now be disabled. A new :program:`configure` option :option:`!--without-freelists` can be used to disable all freelists except empty tuple singleton. diff --git a/Include/internal/pycore_dtoa.h b/Include/internal/pycore_dtoa.h index c77cf6e46cc3c3..a0cfa8cea7d5f6 100644 --- a/Include/internal/pycore_dtoa.h +++ b/Include/internal/pycore_dtoa.h @@ -6,11 +6,6 @@ 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. */ @@ -21,8 +16,6 @@ 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 diff --git a/Include/internal/pycore_pymath.h b/Include/internal/pycore_pymath.h index 5c6aee2a23890b..ecba0387888594 100644 --- a/Include/internal/pycore_pymath.h +++ b/Include/internal/pycore_pymath.h @@ -188,7 +188,7 @@ extern void _Py_set_387controlword(unsigned short); #endif -//--- _PY_SHORT_FLOAT_REPR macro ------------------------------------------- +//--- Make sure that IEEE 754 is supported --------------------------------- // If we can't guarantee 53-bit precision, don't use the code // in Python/dtoa.c, but fall back to standard code. This @@ -203,18 +203,14 @@ extern void _Py_set_387controlword(unsigned short); #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 +# error "Building Python requires IEEE 754 support" #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 +# error "Building Python requires IEEE 754 support" #endif diff --git a/Misc/NEWS.d/next/Build/2022-02-26-01-08-00.bpo-46852.6dJCg1.rst b/Misc/NEWS.d/next/Build/2022-02-26-01-08-00.bpo-46852.6dJCg1.rst new file mode 100644 index 00000000000000..71f47266958fed --- /dev/null +++ b/Misc/NEWS.d/next/Build/2022-02-26-01-08-00.bpo-46852.6dJCg1.rst @@ -0,0 +1,2 @@ +Building Python now requires IEEE 754 (floating point) support. Patch by +Victor Stinner. diff --git a/Misc/NEWS.d/next/Library/2022-02-26-01-08-31.bpo-46852.3SyVbp.rst b/Misc/NEWS.d/next/Library/2022-02-26-01-08-31.bpo-46852.3SyVbp.rst new file mode 100644 index 00000000000000..0460696cc7a2b3 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-02-26-01-08-31.bpo-46852.3SyVbp.rst @@ -0,0 +1,2 @@ +:data:`sys.float_repr_style` is now always ``'short'``. Patch by Victor +Stinner. diff --git a/Modules/cmathmodule.c b/Modules/cmathmodule.c index 2038ac26e65857..7c568a8bb023a3 100644 --- a/Modules/cmathmodule.c +++ b/Modules/cmathmodule.c @@ -7,7 +7,6 @@ #endif #include "Python.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. */ @@ -88,20 +87,10 @@ else { #endif #define CM_SCALE_DOWN (-(CM_SCALE_UP+1)/2) -/* Constants cmath.inf, cmath.infj, cmath.nan, cmath.nanj. - cmath.nan and cmath.nanj are defined only when either - _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) { -#if _PY_SHORT_FLOAT_REPR == 1 return _Py_dg_infinity(0); -#else - return Py_HUGE_VAL; -#endif } static Py_complex @@ -113,16 +102,10 @@ c_infj(void) return r; } -#if _PY_SHORT_FLOAT_REPR == 1 - static double m_nan(void) { -#if _PY_SHORT_FLOAT_REPR == 1 return _Py_dg_stdnan(0); -#else - return Py_NAN; -#endif } static Py_complex @@ -134,8 +117,6 @@ c_nanj(void) return r; } -#endif - /* forward declarations */ static Py_complex cmath_asinh_impl(PyObject *, Py_complex); static Py_complex cmath_atanh_impl(PyObject *, Py_complex); @@ -1282,7 +1263,6 @@ cmath_exec(PyObject *mod) PyComplex_FromCComplex(c_infj())) < 0) { return -1; } -#if _PY_SHORT_FLOAT_REPR == 1 if (PyModule_AddObject(mod, "nan", PyFloat_FromDouble(m_nan())) < 0) { return -1; } @@ -1290,7 +1270,6 @@ cmath_exec(PyObject *mod) PyComplex_FromCComplex(c_nanj())) < 0) { return -1; } -#endif /* initialize special value tables */ diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c index f0aaf23845f6db..ce8dc49118e7cc 100644 --- a/Modules/mathmodule.c +++ b/Modules/mathmodule.c @@ -62,7 +62,6 @@ 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 /* For _Py_log1p with workarounds for buggy handling of zeros. */ @@ -273,30 +272,18 @@ lanczos_sum(double x) static double m_inf(void) { -#if _PY_SHORT_FLOAT_REPR == 1 return _Py_dg_infinity(0); -#else - return Py_HUGE_VAL; -#endif } /* Constant nan value, generated in the same way as float('nan'). */ /* We don't currently assume that Py_NAN is defined everywhere. */ -#if _PY_SHORT_FLOAT_REPR == 1 - static double m_nan(void) { -#if _PY_SHORT_FLOAT_REPR == 1 return _Py_dg_stdnan(0); -#else - return Py_NAN; -#endif } -#endif - static double m_tgamma(double x) { @@ -3838,11 +3825,9 @@ math_exec(PyObject *module) if (PyModule_AddObject(module, "inf", PyFloat_FromDouble(m_inf())) < 0) { return -1; } -#if _PY_SHORT_FLOAT_REPR == 1 if (PyModule_AddObject(module, "nan", PyFloat_FromDouble(m_nan())) < 0) { return -1; } -#endif return 0; } diff --git a/Objects/floatobject.c b/Objects/floatobject.c index 91ca848bf26e8a..91e24056784220 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_SHORT_FLOAT_REPR +#include "pycore_pymath.h" // _Py_SET_53BIT_PRECISION_START #include "pycore_pystate.h" // _PyInterpreterState_GET() #include "pycore_structseq.h" // _PyStructSequence_FiniType() @@ -932,10 +932,8 @@ 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). */ -#if _PY_SHORT_FLOAT_REPR == 1 -/* version of double_round that uses the correctly-rounded string<->double - conversions from Python/dtoa.c */ - +// Rounding that uses the correctly-rounded string<->double conversions +// from Python/dtoa.c static PyObject * double_round(double x, int ndigits) { @@ -989,58 +987,6 @@ double_round(double x, int ndigits) { return result; } -#else // _PY_SHORT_FLOAT_REPR == 0 - -/* fallback version, to be used when correctly rounded binary<->decimal - conversions aren't available */ - -static PyObject * -double_round(double x, int ndigits) { - double pow1, pow2, y, z; - if (ndigits >= 0) { - if (ndigits > 22) { - /* pow1 and pow2 are each safe from overflow, but - pow1*pow2 ~= pow(10.0, ndigits) might overflow */ - pow1 = pow(10.0, (double)(ndigits-22)); - pow2 = 1e22; - } - else { - pow1 = pow(10.0, (double)ndigits); - pow2 = 1.0; - } - y = (x*pow1)*pow2; - /* if y overflows, then rounded value is exactly x */ - if (!Py_IS_FINITE(y)) - return PyFloat_FromDouble(x); - } - else { - pow1 = pow(10.0, (double)-ndigits); - pow2 = 1.0; /* unused; silences a gcc compiler warning */ - y = x / pow1; - } - - z = round(y); - if (fabs(y-z) == 0.5) - /* halfway between two integers; use round-half-even */ - z = 2.0*round(y/2.0); - - if (ndigits >= 0) - z = (z / pow2) / pow1; - else - z *= pow1; - - /* if computation resulted in overflow, raise OverflowError */ - if (!Py_IS_FINITE(z)) { - PyErr_SetString(PyExc_OverflowError, - "overflow occurred during round"); - return NULL; - } - - return PyFloat_FromDouble(z); -} - -#endif // _PY_SHORT_FLOAT_REPR == 0 - /* round a Python float v to the closest multiple of 10**-ndigits */ /*[clinic input] @@ -2407,16 +2353,6 @@ _PyFloat_Unpack2(const unsigned char *p, int le) f |= *p; if (e == 0x1f) { -#if _PY_SHORT_FLOAT_REPR == 0 - if (f == 0) { - /* Infinity */ - return sign ? -Py_HUGE_VAL : Py_HUGE_VAL; - } - else { - /* NaN */ - return sign ? -Py_NAN : Py_NAN; - } -#else // _PY_SHORT_FLOAT_REPR == 1 if (f == 0) { /* Infinity */ return _Py_dg_infinity(sign); @@ -2425,7 +2361,6 @@ _PyFloat_Unpack2(const unsigned char *p, int le) /* NaN */ return _Py_dg_stdnan(sign); } -#endif // _PY_SHORT_FLOAT_REPR == 1 } x = (double)f / 1024.0; diff --git a/Python/dtoa.c b/Python/dtoa.c index 733e70bc791698..2c70a8afe278d7 100644 --- a/Python/dtoa.c +++ b/Python/dtoa.c @@ -118,13 +118,9 @@ /* Linking of Python's #defines to Gay's #defines starts here. */ #include "Python.h" -#include "pycore_dtoa.h" // _PY_SHORT_FLOAT_REPR +#include "pycore_dtoa.h" // _Py_dg_stdnan() #include // exit() -/* if _PY_SHORT_FLOAT_REPR == 0, then don't even try to compile - the following code */ -#if _PY_SHORT_FLOAT_REPR == 1 - #include "float.h" #define MALLOC PyMem_Malloc @@ -2853,8 +2849,7 @@ _Py_dg_dtoa(double dd, int mode, int ndigits, _Py_dg_freedtoa(s0); return NULL; } + #ifdef __cplusplus } #endif - -#endif // _PY_SHORT_FLOAT_REPR == 1 diff --git a/Python/pystrtod.c b/Python/pystrtod.c index 1b27f0a3ad36ad..de7bfd2b40c3d6 100644 --- a/Python/pystrtod.c +++ b/Python/pystrtod.c @@ -2,12 +2,12 @@ #include #include "pycore_dtoa.h" // _Py_dg_strtod() -#include "pycore_pymath.h" // _PY_SHORT_FLOAT_REPR +#include "pycore_pymath.h" // _Py_SET_53BIT_PRECISION_START #include + /* Case-insensitive string match used for nan and inf detection; t should be lower-case. Returns 1 for a successful match, 0 otherwise. */ - static int case_insensitive_match(const char *s, const char *t) { @@ -18,14 +18,12 @@ case_insensitive_match(const char *s, const char *t) return *t ? 0 : 1; } + /* _Py_parse_inf_or_nan: Attempt to parse a string of the form "nan", "inf" or "infinity", with an optional leading sign of "+" or "-". On success, return the NaN or Infinity as a double and set *endptr to point just beyond the successfully parsed portion of the string. On failure, return -1.0 and set *endptr to point to the start of the string. */ - -#if _PY_SHORT_FLOAT_REPR == 1 - double _Py_parse_inf_or_nan(const char *p, char **endptr) { @@ -59,42 +57,6 @@ _Py_parse_inf_or_nan(const char *p, char **endptr) return retval; } -#else - -double -_Py_parse_inf_or_nan(const char *p, char **endptr) -{ - double retval; - const char *s; - int negate = 0; - - s = p; - if (*s == '-') { - negate = 1; - s++; - } - else if (*s == '+') { - s++; - } - if (case_insensitive_match(s, "inf")) { - s += 3; - if (case_insensitive_match(s, "inity")) - s += 5; - retval = negate ? -Py_HUGE_VAL : Py_HUGE_VAL; - } - else if (case_insensitive_match(s, "nan")) { - s += 3; - retval = negate ? -Py_NAN : Py_NAN; - } - else { - s = p; - retval = -1.0; - } - *endptr = (char *)s; - return retval; -} - -#endif /** * _PyOS_ascii_strtod: @@ -124,9 +86,6 @@ _Py_parse_inf_or_nan(const char *p, char **endptr) * * Return value: the #gdouble value. **/ - -#if _PY_SHORT_FLOAT_REPR == 1 - static double _PyOS_ascii_strtod(const char *nptr, char **endptr) { @@ -150,165 +109,6 @@ _PyOS_ascii_strtod(const char *nptr, char **endptr) } -#else - -/* - Use system strtod; since strtod is locale aware, we may - have to first fix the decimal separator. - - Note that unlike _Py_dg_strtod, the system strtod may not always give - correctly rounded results. -*/ - -static double -_PyOS_ascii_strtod(const char *nptr, char **endptr) -{ - char *fail_pos; - double val; - struct lconv *locale_data; - const char *decimal_point; - size_t decimal_point_len; - const char *p, *decimal_point_pos; - const char *end = NULL; /* Silence gcc */ - const char *digits_pos = NULL; - int negate = 0; - - assert(nptr != NULL); - - fail_pos = NULL; - - locale_data = localeconv(); - decimal_point = locale_data->decimal_point; - decimal_point_len = strlen(decimal_point); - - assert(decimal_point_len != 0); - - decimal_point_pos = NULL; - - /* Parse infinities and nans */ - val = _Py_parse_inf_or_nan(nptr, endptr); - if (*endptr != nptr) - return val; - - /* Set errno to zero, so that we can distinguish zero results - and underflows */ - errno = 0; - - /* We process the optional sign manually, then pass the remainder to - the system strtod. This ensures that the result of an underflow - has the correct sign. (bug #1725) */ - p = nptr; - /* Process leading sign, if present */ - if (*p == '-') { - negate = 1; - p++; - } - else if (*p == '+') { - p++; - } - - /* Some platform strtods accept hex floats; Python shouldn't (at the - moment), so we check explicitly for strings starting with '0x'. */ - if (*p == '0' && (*(p+1) == 'x' || *(p+1) == 'X')) - goto invalid_string; - - /* Check that what's left begins with a digit or decimal point */ - if (!Py_ISDIGIT(*p) && *p != '.') - goto invalid_string; - - digits_pos = p; - if (decimal_point[0] != '.' || - decimal_point[1] != 0) - { - /* Look for a '.' in the input; if present, it'll need to be - swapped for the current locale's decimal point before we - call strtod. On the other hand, if we find the current - locale's decimal point then the input is invalid. */ - while (Py_ISDIGIT(*p)) - p++; - - if (*p == '.') - { - decimal_point_pos = p++; - - /* locate end of number */ - while (Py_ISDIGIT(*p)) - p++; - - if (*p == 'e' || *p == 'E') - p++; - if (*p == '+' || *p == '-') - p++; - while (Py_ISDIGIT(*p)) - p++; - end = p; - } - else if (strncmp(p, decimal_point, decimal_point_len) == 0) - /* Python bug #1417699 */ - goto invalid_string; - /* For the other cases, we need not convert the decimal - point */ - } - - if (decimal_point_pos) { - char *copy, *c; - /* Create a copy of the input, with the '.' converted to the - locale-specific decimal point */ - copy = (char *)PyMem_Malloc(end - digits_pos + - 1 + decimal_point_len); - if (copy == NULL) { - *endptr = (char *)nptr; - errno = ENOMEM; - return val; - } - - c = copy; - memcpy(c, digits_pos, decimal_point_pos - digits_pos); - c += decimal_point_pos - digits_pos; - memcpy(c, decimal_point, decimal_point_len); - c += decimal_point_len; - memcpy(c, decimal_point_pos + 1, - end - (decimal_point_pos + 1)); - c += end - (decimal_point_pos + 1); - *c = 0; - - val = strtod(copy, &fail_pos); - - if (fail_pos) - { - if (fail_pos > decimal_point_pos) - fail_pos = (char *)digits_pos + - (fail_pos - copy) - - (decimal_point_len - 1); - else - fail_pos = (char *)digits_pos + - (fail_pos - copy); - } - - PyMem_Free(copy); - - } - else { - val = strtod(digits_pos, &fail_pos); - } - - if (fail_pos == digits_pos) - goto invalid_string; - - if (negate && fail_pos != nptr) - val = -val; - *endptr = fail_pos; - - return val; - - invalid_string: - *endptr = (char*)nptr; - errno = EINVAL; - return -1.0; -} - -#endif - /* PyOS_string_to_double converts a null-terminated byte string s (interpreted as a string of ASCII characters) to a float. The string should not have leading or trailing whitespace. The conversion is independent of the @@ -439,511 +239,6 @@ _Py_string_to_number_with_underscores( return NULL; } -#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 - longer, no need for a maximum buffer size parameter. */ -Py_LOCAL_INLINE(void) -change_decimal_from_locale_to_dot(char* buffer) -{ - struct lconv *locale_data = localeconv(); - const char *decimal_point = locale_data->decimal_point; - - if (decimal_point[0] != '.' || decimal_point[1] != 0) { - size_t decimal_point_len = strlen(decimal_point); - - if (*buffer == '+' || *buffer == '-') - buffer++; - while (Py_ISDIGIT(*buffer)) - buffer++; - if (strncmp(buffer, decimal_point, decimal_point_len) == 0) { - *buffer = '.'; - buffer++; - if (decimal_point_len > 1) { - /* buffer needs to get smaller */ - size_t rest_len = strlen(buffer + - (decimal_point_len - 1)); - memmove(buffer, - buffer + (decimal_point_len - 1), - rest_len); - buffer[rest_len] = 0; - } - } - } -} - - -/* From the C99 standard, section 7.19.6: -The exponent always contains at least two digits, and only as many more digits -as necessary to represent the exponent. -*/ -#define MIN_EXPONENT_DIGITS 2 - -/* Ensure that any exponent, if present, is at least MIN_EXPONENT_DIGITS - in length. */ -Py_LOCAL_INLINE(void) -ensure_minimum_exponent_length(char* buffer, size_t buf_size) -{ - char *p = strpbrk(buffer, "eE"); - if (p && (*(p + 1) == '-' || *(p + 1) == '+')) { - char *start = p + 2; - int exponent_digit_cnt = 0; - int leading_zero_cnt = 0; - int in_leading_zeros = 1; - int significant_digit_cnt; - - /* Skip over the exponent and the sign. */ - p += 2; - - /* Find the end of the exponent, keeping track of leading - zeros. */ - while (*p && Py_ISDIGIT(*p)) { - if (in_leading_zeros && *p == '0') - ++leading_zero_cnt; - if (*p != '0') - in_leading_zeros = 0; - ++p; - ++exponent_digit_cnt; - } - - significant_digit_cnt = exponent_digit_cnt - leading_zero_cnt; - if (exponent_digit_cnt == MIN_EXPONENT_DIGITS) { - /* If there are 2 exactly digits, we're done, - regardless of what they contain */ - } - else if (exponent_digit_cnt > MIN_EXPONENT_DIGITS) { - int extra_zeros_cnt; - - /* There are more than 2 digits in the exponent. See - if we can delete some of the leading zeros */ - if (significant_digit_cnt < MIN_EXPONENT_DIGITS) - significant_digit_cnt = MIN_EXPONENT_DIGITS; - extra_zeros_cnt = exponent_digit_cnt - - significant_digit_cnt; - - /* Delete extra_zeros_cnt worth of characters from the - front of the exponent */ - assert(extra_zeros_cnt >= 0); - - /* Add one to significant_digit_cnt to copy the - trailing 0 byte, thus setting the length */ - memmove(start, - start + extra_zeros_cnt, - significant_digit_cnt + 1); - } - else { - /* If there are fewer than 2 digits, add zeros - until there are 2, if there's enough room */ - int zeros = MIN_EXPONENT_DIGITS - exponent_digit_cnt; - if (start + zeros + exponent_digit_cnt + 1 - < buffer + buf_size) { - memmove(start + zeros, start, - exponent_digit_cnt + 1); - memset(start, '0', zeros); - } - } - } -} - -/* Remove trailing zeros after the decimal point from a numeric string; also - remove the decimal point if all digits following it are zero. The numeric - string must end in '\0', and should not have any leading or trailing - whitespace. Assumes that the decimal point is '.'. */ -Py_LOCAL_INLINE(void) -remove_trailing_zeros(char *buffer) -{ - char *old_fraction_end, *new_fraction_end, *end, *p; - - p = buffer; - if (*p == '-' || *p == '+') - /* Skip leading sign, if present */ - ++p; - while (Py_ISDIGIT(*p)) - ++p; - - /* if there's no decimal point there's nothing to do */ - if (*p++ != '.') - return; - - /* scan any digits after the point */ - while (Py_ISDIGIT(*p)) - ++p; - old_fraction_end = p; - - /* scan up to ending '\0' */ - while (*p != '\0') - p++; - /* +1 to make sure that we move the null byte as well */ - end = p+1; - - /* scan back from fraction_end, looking for removable zeros */ - p = old_fraction_end; - while (*(p-1) == '0') - --p; - /* and remove point if we've got that far */ - if (*(p-1) == '.') - --p; - new_fraction_end = p; - - memmove(new_fraction_end, old_fraction_end, end-old_fraction_end); -} - -/* Ensure that buffer has a decimal point in it. The decimal point will not - be in the current locale, it will always be '.'. Don't add a decimal point - if an exponent is present. Also, convert to exponential notation where - adding a '.0' would produce too many significant digits (see issue 5864). - - Returns a pointer to the fixed buffer, or NULL on failure. -*/ -Py_LOCAL_INLINE(char *) -ensure_decimal_point(char* buffer, size_t buf_size, int precision) -{ - int digit_count, insert_count = 0, convert_to_exp = 0; - const char *chars_to_insert; - char *digits_start; - - /* search for the first non-digit character */ - char *p = buffer; - if (*p == '-' || *p == '+') - /* Skip leading sign, if present. I think this could only - ever be '-', but it can't hurt to check for both. */ - ++p; - digits_start = p; - while (*p && Py_ISDIGIT(*p)) - ++p; - digit_count = Py_SAFE_DOWNCAST(p - digits_start, Py_ssize_t, int); - - if (*p == '.') { - if (Py_ISDIGIT(*(p+1))) { - /* Nothing to do, we already have a decimal - point and a digit after it */ - } - else { - /* We have a decimal point, but no following - digit. Insert a zero after the decimal. */ - /* can't ever get here via PyOS_double_to_string */ - assert(precision == -1); - ++p; - chars_to_insert = "0"; - insert_count = 1; - } - } - else if (!(*p == 'e' || *p == 'E')) { - /* Don't add ".0" if we have an exponent. */ - if (digit_count == precision) { - /* issue 5864: don't add a trailing .0 in the case - where the '%g'-formatted result already has as many - significant digits as were requested. Switch to - exponential notation instead. */ - convert_to_exp = 1; - /* no exponent, no point, and we shouldn't land here - for infs and nans, so we must be at the end of the - string. */ - assert(*p == '\0'); - } - else { - assert(precision == -1 || digit_count < precision); - chars_to_insert = ".0"; - insert_count = 2; - } - } - if (insert_count) { - size_t buf_len = strlen(buffer); - if (buf_len + insert_count + 1 >= buf_size) { - /* If there is not enough room in the buffer - for the additional text, just skip it. It's - not worth generating an error over. */ - } - else { - memmove(p + insert_count, p, - buffer + strlen(buffer) - p + 1); - memcpy(p, chars_to_insert, insert_count); - } - } - if (convert_to_exp) { - int written; - size_t buf_avail; - p = digits_start; - /* insert decimal point */ - assert(digit_count >= 1); - memmove(p+2, p+1, digit_count); /* safe, but overwrites nul */ - p[1] = '.'; - p += digit_count+1; - assert(p <= buf_size+buffer); - buf_avail = buf_size+buffer-p; - if (buf_avail == 0) - return NULL; - /* Add exponent. It's okay to use lower case 'e': we only - arrive here as a result of using the empty format code or - repr/str builtins and those never want an upper case 'E' */ - written = PyOS_snprintf(p, buf_avail, "e%+.02d", digit_count-1); - if (!(0 <= written && - written < Py_SAFE_DOWNCAST(buf_avail, size_t, int))) - /* output truncated, or something else bad happened */ - return NULL; - remove_trailing_zeros(buffer); - } - return buffer; -} - -/* see FORMATBUFLEN in unicodeobject.c */ -#define FLOAT_FORMATBUFLEN 120 - -/** - * _PyOS_ascii_formatd: - * @buffer: A buffer to place the resulting string in - * @buf_size: The length of the buffer. - * @format: The printf()-style format to use for the - * code to use for converting. - * @d: The #gdouble to convert - * @precision: The precision to use when formatting. - * - * Converts a #gdouble to a string, using the '.' as - * decimal point. To format the number you pass in - * a printf()-style format string. Allowed conversion - * specifiers are 'e', 'E', 'f', 'F', 'g', 'G', and 'Z'. - * - * 'Z' is the same as 'g', except it always has a decimal and - * at least one digit after the decimal. - * - * Return value: The pointer to the buffer with the converted string. - * On failure returns NULL but does not set any Python exception. - **/ -static char * -_PyOS_ascii_formatd(char *buffer, - size_t buf_size, - const char *format, - double d, - int precision) -{ - char format_char; - size_t format_len = strlen(format); - - /* Issue 2264: code 'Z' requires copying the format. 'Z' is 'g', but - also with at least one character past the decimal. */ - char tmp_format[FLOAT_FORMATBUFLEN]; - - /* The last character in the format string must be the format char */ - format_char = format[format_len - 1]; - - if (format[0] != '%') - return NULL; - - /* I'm not sure why this test is here. It's ensuring that the format - string after the first character doesn't have a single quote, a - lowercase l, or a percent. This is the reverse of the commented-out - test about 10 lines ago. */ - if (strpbrk(format + 1, "'l%")) - return NULL; - - /* Also curious about this function is that it accepts format strings - like "%xg", which are invalid for floats. In general, the - interface to this function is not very good, but changing it is - difficult because it's a public API. */ - - if (!(format_char == 'e' || format_char == 'E' || - format_char == 'f' || format_char == 'F' || - format_char == 'g' || format_char == 'G' || - format_char == 'Z')) - return NULL; - - /* Map 'Z' format_char to 'g', by copying the format string and - replacing the final char with a 'g' */ - if (format_char == 'Z') { - if (format_len + 1 >= sizeof(tmp_format)) { - /* The format won't fit in our copy. Error out. In - practice, this will never happen and will be - detected by returning NULL */ - return NULL; - } - strcpy(tmp_format, format); - tmp_format[format_len - 1] = 'g'; - format = tmp_format; - } - - - /* Have PyOS_snprintf do the hard work */ - PyOS_snprintf(buffer, buf_size, format, d); - - /* Do various fixups on the return string */ - - /* Get the current locale, and find the decimal point string. - Convert that string back to a dot. */ - change_decimal_from_locale_to_dot(buffer); - - /* If an exponent exists, ensure that the exponent is at least - MIN_EXPONENT_DIGITS digits, providing the buffer is large enough - for the extra zeros. Also, if there are more than - MIN_EXPONENT_DIGITS, remove as many zeros as possible until we get - back to MIN_EXPONENT_DIGITS */ - ensure_minimum_exponent_length(buffer, buf_size); - - /* If format_char is 'Z', make sure we have at least one character - after the decimal point (and make sure we have a decimal point); - also switch to exponential notation in some edge cases where the - extra character would produce more significant digits that we - really want. */ - if (format_char == 'Z') - buffer = ensure_decimal_point(buffer, buf_size, precision); - - return buffer; -} - -/* The fallback code to use if _Py_dg_dtoa is not available. */ - -char * PyOS_double_to_string(double val, - char format_code, - int precision, - int flags, - int *type) -{ - char format[32]; - Py_ssize_t bufsize; - char *buf; - int t, exp; - int upper = 0; - - /* Validate format_code, and map upper and lower case */ - switch (format_code) { - case 'e': /* exponent */ - case 'f': /* fixed */ - case 'g': /* general */ - break; - case 'E': - upper = 1; - format_code = 'e'; - break; - case 'F': - upper = 1; - format_code = 'f'; - break; - case 'G': - upper = 1; - format_code = 'g'; - break; - case 'r': /* repr format */ - /* Supplied precision is unused, must be 0. */ - if (precision != 0) { - PyErr_BadInternalCall(); - return NULL; - } - /* The repr() precision (17 significant decimal digits) is the - minimal number that is guaranteed to have enough precision - so that if the number is read back in the exact same binary - value is recreated. This is true for IEEE floating point - by design, and also happens to work for all other modern - hardware. */ - precision = 17; - format_code = 'g'; - break; - default: - PyErr_BadInternalCall(); - return NULL; - } - - /* Here's a quick-and-dirty calculation to figure out how big a buffer - we need. In general, for a finite float we need: - - 1 byte for each digit of the decimal significand, and - - 1 for a possible sign - 1 for a possible decimal point - 2 for a possible [eE][+-] - 1 for each digit of the exponent; if we allow 19 digits - total then we're safe up to exponents of 2**63. - 1 for the trailing nul byte - - This gives a total of 24 + the number of digits in the significand, - and the number of digits in the significand is: - - for 'g' format: at most precision, except possibly - when precision == 0, when it's 1. - for 'e' format: precision+1 - for 'f' format: precision digits after the point, at least 1 - before. To figure out how many digits appear before the point - we have to examine the size of the number. If fabs(val) < 1.0 - then there will be only one digit before the point. If - fabs(val) >= 1.0, then there are at most - - 1+floor(log10(ceiling(fabs(val)))) - - digits before the point (where the 'ceiling' allows for the - possibility that the rounding rounds the integer part of val - up). A safe upper bound for the above quantity is - 1+floor(exp/3), where exp is the unique integer such that 0.5 - <= fabs(val)/2**exp < 1.0. This exp can be obtained from - frexp. - - So we allow room for precision+1 digits for all formats, plus an - extra floor(exp/3) digits for 'f' format. - - */ - - if (Py_IS_NAN(val) || Py_IS_INFINITY(val)) - /* 3 for 'inf'/'nan', 1 for sign, 1 for '\0' */ - bufsize = 5; - else { - bufsize = 25 + precision; - if (format_code == 'f' && fabs(val) >= 1.0) { - frexp(val, &exp); - bufsize += exp/3; - } - } - - buf = PyMem_Malloc(bufsize); - if (buf == NULL) { - PyErr_NoMemory(); - return NULL; - } - - /* Handle nan and inf. */ - if (Py_IS_NAN(val)) { - strcpy(buf, "nan"); - t = Py_DTST_NAN; - } else if (Py_IS_INFINITY(val)) { - if (copysign(1., val) == 1.) - strcpy(buf, "inf"); - else - strcpy(buf, "-inf"); - t = Py_DTST_INFINITE; - } else { - t = Py_DTST_FINITE; - if (flags & Py_DTSF_ADD_DOT_0) - format_code = 'Z'; - - PyOS_snprintf(format, sizeof(format), "%%%s.%i%c", - (flags & Py_DTSF_ALT ? "#" : ""), precision, - format_code); - _PyOS_ascii_formatd(buf, bufsize, format, val, precision); - } - - /* Add sign when requested. It's convenient (esp. when formatting - complex numbers) to include a sign even for inf and nan. */ - if (flags & Py_DTSF_SIGN && buf[0] != '-') { - size_t len = strlen(buf); - /* the bufsize calculations above should ensure that we've got - space to add a sign */ - assert((size_t)bufsize >= len+2); - memmove(buf+1, buf, len+1); - buf[0] = '+'; - } - if (upper) { - /* Convert to upper case. */ - char *p1; - for (p1 = buf; *p1; p1++) - *p1 = Py_TOUPPER(*p1); - } - - if (type) - *type = t; - return buf; -} - -#else // _PY_SHORT_FLOAT_REPR == 1 - -/* _Py_dg_dtoa is available. */ - /* I'm using a lookup table here so that I don't have to invent a non-locale specific way to convert to uppercase */ #define OFS_INF 0 @@ -1303,4 +598,3 @@ char * PyOS_double_to_string(double val, flags & Py_DTSF_ALT, float_strings, type); } -#endif // _PY_SHORT_FLOAT_REPR == 1 diff --git a/Python/sysmodule.c b/Python/sysmodule.c index a97d0341ddcfd7..e1aabb06ab173a 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -25,7 +25,6 @@ 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() @@ -2838,11 +2837,7 @@ _PySys_InitCore(PyThreadState *tstate, PyObject *sysdict) #endif /* float repr style: 0.03 (short) vs 0.029999999999999999 (legacy) */ -#if _PY_SHORT_FLOAT_REPR == 1 SET_SYS_FROM_STRING("float_repr_style", "short"); -#else - SET_SYS_FROM_STRING("float_repr_style", "legacy"); -#endif SET_SYS("thread_info", PyThread_GetInfo());