summaryrefslogtreecommitdiffstats
path: root/generic/tclUtil.c
diff options
context:
space:
mode:
authorKevin B Kenny <kennykb@acm.org>2010-11-28 23:20:10 (GMT)
committerKevin B Kenny <kennykb@acm.org>2010-11-28 23:20:10 (GMT)
commita5a72404b019601daeadb7184189dd40ef6ac90c (patch)
tree87833950b8671a111d7d2e3d6ba8194c7359cb36 /generic/tclUtil.c
parentabe7eadae6ebae4c2827f9314f7d81af9dfff916 (diff)
downloadtcl-a5a72404b019601daeadb7184189dd40ef6ac90c.zip
tcl-a5a72404b019601daeadb7184189dd40ef6ac90c.tar.gz
tcl-a5a72404b019601daeadb7184189dd40ef6ac90c.tar.bz2
2010-11-29 Kevin B. Kenny <kennykb@acm.org>
* generic/tclInt.decls: * generic/tclInt.h: * generic/tclStrToD.c: * generic/tclTest.c: * generic/tclTomMath.decls: * generic/tclUtil.c: * tests/util.test: * unix/Makefile.in: * win/Makefile.in: * win/makefile.vc: Rewrite of Tcl_PrintDouble and TclDoubleDigits that (a) fixes a severe performance problem with floating point shimmering reported by Karl Lehenbauer, (b) allows TclDoubleDigits to generate the digit strings for 'e' and 'f' format, so that it can be used for tcl_precision != 0 (and possibly later for [format]), (c) fixes [Bug 3120139] by making TclPrintDouble inherently locale-independent, (d) adds test cases to util.test for correct rounding in difficult cases of TclDoubleDigits where fixed- precision results are requested. (e) adds test cases to util.test for the controversial aspects of [Bug 3105247]. As a side effect, two more modules from libtommath (bn_mp_set_int.c and bn_mp_init_set_int.c) are brought into the build, since the new code uses them.
Diffstat (limited to 'generic/tclUtil.c')
-rw-r--r--generic/tclUtil.c167
1 files changed, 69 insertions, 98 deletions
diff --git a/generic/tclUtil.c b/generic/tclUtil.c
index 45cc8a1..a04c29c 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.118 2010/10/01 12:52:50 dkf Exp $
+ * RCS: @(#) $Id: tclUtil.c,v 1.119 2010/11/28 23:20:11 kennykb Exp $
*/
#include "tclInt.h"
@@ -2234,129 +2234,100 @@ Tcl_PrintDouble(
char *p, c;
int exponent;
int signum;
- char buffer[TCL_DOUBLE_SPACE];
+ char* digits;
+ char* end;
Tcl_UniChar ch;
int *precisionPtr = Tcl_GetThreadData(&precisionKey, (int)sizeof(int));
/*
- * If *precisionPtr == 0, then use TclDoubleDigits to develop a decimal
- * significand and exponent, then format it in E or F format as
- * appropriate. If *precisionPtr != 0, use the native sprintf and then add
- * a trailing ".0" if there is no decimal point in the rep.
+ * Handle NaN.
*/
+
+ if (TclIsNaN(value)) {
+ TclFormatNaN(value, dst);
+ return;
+ }
- if (*precisionPtr == 0) {
+ /*
+ * Handle infinities.
+ */
+
+ if (TclIsInfinite(value)) {
/*
- * Handle NaN.
+ * Remember to copy the terminating NUL too.
*/
-
- if (TclIsNaN(value)) {
- TclFormatNaN(value, dst);
- return;
+
+ if (value < 0) {
+ memcpy(dst, "-Inf", 5);
+ } else {
+ memcpy(dst, "Inf", 4);
}
+ return;
+ }
+ /*
+ * Ordinary (normal and denormal) values.
+ */
+
+ if (*precisionPtr == 0) {
+ digits = TclDoubleDigits(value, -1, TCL_DD_SHORTEST,
+ &exponent, &signum, &end);
+ } else {
+ digits = TclDoubleDigits(value, *precisionPtr, TCL_DD_E_FORMAT,
+ &exponent, &signum, &end);
+ }
+ if (signum) {
+ *dst++ = '-';
+ }
+ p = digits;
+ if (exponent < -4 || exponent > 16) {
/*
- * Handle infinities.
+ * E format for numbers < 1e-3 or >= 1e17.
*/
-
- if (TclIsInfinite(value)) {
- /*
- * Remember to copy the terminating NUL too.
- */
-
- if (value < 0) {
- memcpy(dst, "-Inf", 5);
- } else {
- memcpy(dst, "Inf", 4);
+
+ *dst++ = *p++;
+ c = *p;
+ if (c != '\0') {
+ *dst++ = '.';
+ while (c != '\0') {
+ *dst++ = c;
+ c = *++p;
}
- return;
}
-
+ sprintf(dst, "e%+d", exponent);
+ } else {
/*
- * Ordinary (normal and denormal) values.
+ * F format for others.
*/
-
- exponent = TclDoubleDigits(buffer, value, &signum);
- if (signum) {
- *dst++ = '-';
+
+ if (exponent < 0) {
+ *dst++ = '0';
}
- p = buffer;
- if (exponent < -3 || exponent > 17) {
- /*
- * E format for numbers < 1e-3 or >= 1e17.
- */
-
- *dst++ = *p++;
- c = *p;
+ c = *p;
+ while (exponent-- >= 0) {
if (c != '\0') {
- *dst++ = '.';
- while (c != '\0') {
- *dst++ = c;
- c = *++p;
- }
- }
- sprintf(dst, "e%+d", exponent-1);
- } else {
- /*
- * F format for others.
- */
-
- if (exponent <= 0) {
- *dst++ = '0';
- }
- c = *p;
- while (exponent-- > 0) {
- if (c != '\0') {
- *dst++ = c;
- c = *++p;
- } else {
- *dst++ = '0';
- }
- }
- *dst++ = '.';
- if (c == '\0') {
- *dst++ = '0';
+ *dst++ = c;
+ c = *++p;
} else {
- while (++exponent < 0) {
- *dst++ = '0';
- }
- while (c != '\0') {
- *dst++ = c;
- c = *++p;
- }
+ *dst++ = '0';
}
- *dst++ = '\0';
}
- } else {
- /*
- * tcl_precision is supplied, pass it to the native sprintf.
- */
-
- sprintf(dst, "%.*g", *precisionPtr, value);
-
- /*
- * If the ASCII result looks like an integer, add ".0" so that it
- * doesn't look like an integer anymore. This prevents floating-point
- * values from being converted to integers unintentionally. Check for
- * ASCII specifically to speed up the function.
- */
-
- for (p = dst; *p != 0;) {
- if (UCHAR(*p) < 0x80) {
- c = *p++;
- } else {
- p += Tcl_UtfToUniChar(p, &ch);
- c = UCHAR(ch);
+ *dst++ = '.';
+ if (c == '\0') {
+ *dst++ = '0';
+ } else {
+ while (++exponent < -1) {
+ *dst++ = '0';
}
- if ((c == '.') || isalpha(UCHAR(c))) { /* INTL: ISO only. */
- return;
+ while (c != '\0') {
+ *dst++ = c;
+ c = *++p;
}
}
- p[0] = '.';
- p[1] = '0';
- p[2] = 0;
+ *dst++ = '\0';
}
+ ckfree(digits);
}
/*