Commit 3370cce4 authored by Mark Dickinson's avatar Mark Dickinson

Issue 5780: Fix test_float failures for legacy style float repr.

parent 7efad9ec
......@@ -20,14 +20,7 @@ Core and Builtins
- Implement PEP 378, Format Specifier for Thousands Separator, for
floats.
- The repr function switches to exponential notation at 1e16, not 1e17
as it did before. This change applies to both 'short' and legacy
float repr styles. For the new repr style, it avoids misleading
output in some cases: an example is repr(2e16+8), which gives
'2.000000000000001e+16'; without this change it would have produced
'20000000000000010.0' instead.
- Similarly, the str function switches to exponential notation at
- The str function switches to exponential notation at
1e11, not 1e12. This avoids printing 13 significant digits in
situations where only 12 of them are correct. Example problem
value: str(1e11 + 0.5). (This minor issue has existed in 2.x for a
......@@ -44,6 +37,9 @@ Core and Builtins
finite float x, repr(x) now outputs a string based on the shortest
sequence of decimal digits that rounds to x. Previous behaviour was
to output 17 significant digits and then strip trailing zeros.
Another minor difference is that the new repr switches to
exponential notation at 1e16 instead of the previous 1e17; this
avoids misleading output in some cases.
There's a new sys attribute sys.float_repr_style, which takes
the value 'short' to indicate that we're using short float repr,
......
......@@ -485,6 +485,50 @@ PyOS_ascii_formatd(char *buffer,
/* The fallback code to use if _Py_dg_dtoa is not available. */
/* 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 (isdigit(Py_CHARMASK(*p)))
++p;
/* if there's no decimal point there's nothing to do */
if (*p++ != '.')
return;
/* scan any digits after the point */
while (isdigit(Py_CHARMASK(*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);
}
PyAPI_FUNC(char *) PyOS_double_to_string(double val,
char format_code,
int precision,
......@@ -498,6 +542,7 @@ PyAPI_FUNC(char *) PyOS_double_to_string(double val,
char *p;
int t;
int upper = 0;
int strip_trailing_zeros = 0;
/* Validate format_code, and map upper and lower case */
switch (format_code) {
......@@ -532,8 +577,17 @@ PyAPI_FUNC(char *) PyOS_double_to_string(double val,
PyErr_BadInternalCall();
return NULL;
}
precision = 12;
format_code = 'g';
/* switch to exponential notation at 1e11, or 1e12 if we're
not adding a .0 */
if (fabs(val) >= (flags & Py_DTSF_ADD_DOT_0 ? 1e11 : 1e12)) {
precision = 11;
format_code = 'e';
strip_trailing_zeros = 1;
}
else {
precision = 12;
format_code = 'g';
}
break;
default:
PyErr_BadInternalCall();
......@@ -554,11 +608,14 @@ PyAPI_FUNC(char *) PyOS_double_to_string(double val,
t = Py_DTST_FINITE;
if (flags & Py_DTSF_ADD_DOT_0)
if ((flags & Py_DTSF_ADD_DOT_0) && (format_code != 'e'))
format_code = 'Z';
PyOS_snprintf(format, 32, "%%%s.%i%c", (flags & Py_DTSF_ALT ? "#" : ""), precision, format_code);
PyOS_ascii_formatd(buf, sizeof(buf), format, val);
/* remove trailing zeros if necessary */
if (strip_trailing_zeros)
remove_trailing_zeros(buf);
}
len = strlen(buf);
......@@ -671,7 +728,7 @@ format_float_short(double d, char format_code,
assert(digits_end != NULL && digits_end >= digits);
digits_len = digits_end - digits;
if (digits_len && !isdigit(digits[0])) {
if (digits_len && !isdigit(Py_CHARMASK(digits[0]))) {
/* Infinities and nans here; adapt Gay's output,
so convert Infinity to inf and NaN to nan, and
ignore sign of nan. Then return. */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment