summaryrefslogtreecommitdiffstats
path: root/generic
diff options
context:
space:
mode:
authorKevin B Kenny <kennykb@acm.org>2011-01-15 19:07:01 (GMT)
committerKevin B Kenny <kennykb@acm.org>2011-01-15 19:07:01 (GMT)
commitc342aae64b0aa06acf5b0e56ac8809572824ee26 (patch)
tree0974bfd4c963917887bf30cec9330b5cfe4e0193 /generic
parent63b366c72bff6d044874c992d5a767695c977b22 (diff)
downloadtcl-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-xgeneric/tclStrToD.c30
-rw-r--r--generic/tclUtil.c126
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);
}