]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
Issue 5780: Fix test_float failures for legacy style float repr.
authorMark Dickinson <dickinsm@gmail.com>
Fri, 17 Apr 2009 22:40:53 +0000 (22:40 +0000)
committerMark Dickinson <dickinsm@gmail.com>
Fri, 17 Apr 2009 22:40:53 +0000 (22:40 +0000)
Misc/NEWS
Python/pystrtod.c

index d4774dda9fcffba5f1417a41729b8a864ca56dbc..f0ed225cb64091421047ede67dd9827afe6dd40e 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -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,
index 9e00823fad8993c40af3c4e66edbc07e5604e8aa..95fbd8915203cd3814c77b4e0f58e2d3e36667b3 100644 (file)
@@ -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. */