diff options
author | Kevin B Kenny <kennykb@acm.org> | 2011-01-15 19:07:01 (GMT) |
---|---|---|
committer | Kevin B Kenny <kennykb@acm.org> | 2011-01-15 19:07:01 (GMT) |
commit | c342aae64b0aa06acf5b0e56ac8809572824ee26 (patch) | |
tree | 0974bfd4c963917887bf30cec9330b5cfe4e0193 /generic | |
parent | 63b366c72bff6d044874c992d5a767695c977b22 (diff) | |
download | tcl-c342aae64b0aa06acf5b0e56ac8809572824ee26.zip tcl-c342aae64b0aa06acf5b0e56ac8809572824ee26.tar.gz tcl-c342aae64b0aa06acf5b0e56ac8809572824ee26.tar.bz2 |
* doc/tclvars.n:
* generic/tclStrToD.c:
* generic/tclUtil.c (Tcl_PrintDouble):
* tests/util.test (util-16.*): Restored full Tcl 8.4 compatibility
for the formatting of floating point numbers when $::tcl_precision
is not zero. Added compatibility tests to make sure that excess
trailing zeroes are suppressed for all eight major code paths.
[Bug 3157475]
Diffstat (limited to 'generic')
-rwxr-xr-x | generic/tclStrToD.c | 30 | ||||
-rw-r--r-- | generic/tclUtil.c | 126 |
2 files changed, 110 insertions, 46 deletions
diff --git a/generic/tclStrToD.c b/generic/tclStrToD.c index df2919c..ed2c59c 100755 --- a/generic/tclStrToD.c +++ b/generic/tclStrToD.c @@ -14,7 +14,7 @@ * See the file "license.terms" for information on usage and redistribution of * this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tclStrToD.c,v 1.33.2.7 2010/12/01 18:57:11 andreas_kupries Exp $ + * RCS: @(#) $Id: tclStrToD.c,v 1.33.2.8 2011/01/15 19:07:01 kennykb Exp $ * *---------------------------------------------------------------------- */ @@ -3046,6 +3046,11 @@ StrictInt64Conversion(Double* dPtr, if (2*b > S || (2*b == S && (digit & 1) != 0)) { s = BumpUp(s, retval, &k); + } else { + while (*--s == '0') { + /* do nothing */ + } + ++s; } break; } @@ -3463,6 +3468,11 @@ StrictBignumConversionPowD(Double* dPtr, if (i == ilim) { if (ShouldBankerRoundUpPowD(&b, sd, digit&1)) { s = BumpUp(s, retval, &k); + } else { + while (*--s == '0') { + /* do nothing */ + } + ++s; } break; } @@ -3736,7 +3746,7 @@ ShorteningBignumConversion(Double* dPtr, mp_div_d(&S, 5, &S, NULL); --s5; /* - * TODO: It might possibly be a win to fall back to + * IDEA: It might possibly be a win to fall back to * int64 arithmetic here if S < 2**64/10. But it's * a win only for a fairly narrow range of magnitudes * so perhaps not worth bothering. We already know that @@ -3934,8 +3944,13 @@ StrictBignumConversion(Double* dPtr, mp_mul_2d(&b, 1, &b); if (ShouldBankerRoundUp(&b, &S, digit&1)) { s = BumpUp(s, retval, &k); - } - break; + } else { + while (*--s == '0') { + /* do nothing */ + } + ++s; + } + break; } } } @@ -3996,9 +4011,9 @@ StrictBignumConversion(Double* dPtr, * given number (and resolving ties with 'round to even'). * It is allowed to return fewer than 'ndigits' if the number * converts exactly; if the TCL_DD_E_FORMAT|TCL_DD_SHORTEN_FLAG - * is supplied instead, it is also allowed to return fewer digits - * if the shorter string will still reconvert to the given - * input number. + * is supplied instead, it also returns fewer digits if the + * shorter string will still reconvert to the given input number. + * In any case, strings of trailing zeroes are suppressed. * TCL_DD_F_FORMAT - This value is used to prepare numbers for %f * format conversion. It requests that conversion proceed until * 'ndigits' digits after the decimal point have been converted. @@ -4009,6 +4024,7 @@ StrictBignumConversion(Double* dPtr, * argument to TCL_DD_F_FORMAT|TCL_DD_SHORTEN_FLAG will allow * the routine also to return fewer digits if the shorter string * will still reconvert without loss to the given input number. + * Strings of trailing zeroes are suppressed. * * To any of these flags may be OR'ed TCL_DD_NO_QUICK; this flag * requires all calculations to be done in exact arithmetic. Normally, diff --git a/generic/tclUtil.c b/generic/tclUtil.c index ee17166..ff2fb6f 100644 --- a/generic/tclUtil.c +++ b/generic/tclUtil.c @@ -11,7 +11,7 @@ * See the file "license.terms" for information on usage and redistribution of * this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tclUtil.c,v 1.97.2.9 2010/12/03 22:27:44 hobbs Exp $ + * RCS: @(#) $Id: tclUtil.c,v 1.97.2.10 2011/01/15 19:07:01 kennykb Exp $ */ #include "tclInt.h" @@ -2273,7 +2273,47 @@ Tcl_PrintDouble( digits = TclDoubleDigits(value, -1, TCL_DD_SHORTEST, &exponent, &signum, &end); } else { - digits = TclDoubleDigits(value, *precisionPtr, TCL_DD_E_FORMAT, + /* + * There are at least two possible interpretations for tcl_precision. + * + * The first is, "choose the decimal representation having + * $tcl_precision digits of significance that is nearest to the + * given number, breaking ties by rounding to even, and then + * trimming trailing zeros." This gives the greatest possible + * precision in the decimal string, but offers the anomaly that + * [expr 0.1] will be "0.10000000000000001". + * + * The second is "choose the decimal representation having at + * most $tcl_precision digits of significance that is nearest + * to the given number. If no such representation converts + * exactly to the given number, choose the one that is closest, + * breaking ties by rounding to even. If more than one such + * representation converts exactly to the given number, choose + * the shortest, breaking ties in favour of the nearest, breaking + * remaining ties in favour of the one ending in an even digit." + * + * Tcl 8.4 implements the first of these, which gives rise to + * anomalies in formatting: + * + * % expr 0.1 + * 0.10000000000000001 + * % expr 0.01 + * 0.01 + * % expr 1e-7 + * 9.9999999999999995e-08 + * + * For human readability, it appears better to choose the second rule, + * and let [expr 0.1] return 0.1. But for 8.4 compatibility, we + * prefer the first (the recommended zero value for tcl_precision + * avoids the problem entirely). + * + * Uncomment TCL_DD_SHORTEN_FLAG in the next call to prefer the + * method that allows floating point values to be shortened if + * it can be done without loss of precision. + */ + + digits = TclDoubleDigits(value, *precisionPtr, + TCL_DD_E_FORMAT /* | TCL_DD_SHORTEN_FLAG */, &exponent, &signum, &end); } if (signum) { @@ -2281,51 +2321,59 @@ Tcl_PrintDouble( } p = digits; if (exponent < -4 || exponent > 16) { - /* - * E format for numbers < 1e-3 or >= 1e17. - */ - - *dst++ = *p++; - c = *p; - if (c != '\0') { - *dst++ = '.'; - while (c != '\0') { - *dst++ = c; - c = *++p; - } + /* + * E format for numbers < 1e-3 or >= 1e17. + */ + + *dst++ = *p++; + c = *p; + if (c != '\0') { + *dst++ = '.'; + while (c != '\0') { + *dst++ = c; + c = *++p; } - sprintf(dst, "e%+d", exponent); + } + /* + * Tcl 8.4 appears to format with at least a two-digit exponent; \ + * preserve that behaviour when tcl_precision != 0 + */ + if (*precisionPtr == 0) { + sprintf(dst, "e%+d", exponent); } else { - /* - * F format for others. - */ - + sprintf(dst, "e%+03d", exponent); + } + } else { + /* + * F format for others. + */ + if (exponent < 0) { - *dst++ = '0'; - } - c = *p; + *dst++ = '0'; + } + c = *p; while (exponent-- >= 0) { - if (c != '\0') { - *dst++ = c; - c = *++p; - } else { - *dst++ = '0'; - } - } - *dst++ = '.'; - if (c == '\0') { - *dst++ = '0'; + if (c != '\0') { + *dst++ = c; + c = *++p; } else { + *dst++ = '0'; + } + } + *dst++ = '.'; + if (c == '\0') { + *dst++ = '0'; + } else { while (++exponent < -1) { - *dst++ = '0'; - } - while (c != '\0') { - *dst++ = c; - c = *++p; - } + *dst++ = '0'; + } + while (c != '\0') { + *dst++ = c; + c = *++p; } - *dst++ = '\0'; } + *dst++ = '\0'; + } ckfree(digits); } |