Index: Python/pystrtod.c =================================================================== --- Python/pystrtod.c (revision 71669) +++ Python/pystrtod.c (working copy) @@ -384,6 +384,50 @@ } } +/* 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); +} + + /* see FORMATBUFLEN in unicodeobject.c */ #define FLOAT_FORMATBUFLEN 120 @@ -498,6 +542,7 @@ 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 @@ 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 @@ 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); Index: Misc/NEWS =================================================================== --- Misc/NEWS (revision 71669) +++ Misc/NEWS (working copy) @@ -20,14 +20,7 @@ - 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 @@ 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,