summaryrefslogtreecommitdiffstats
path: root/generic/tclUtil.c
diff options
context:
space:
mode:
Diffstat (limited to 'generic/tclUtil.c')
-rw-r--r--generic/tclUtil.c188
1 files changed, 147 insertions, 41 deletions
diff --git a/generic/tclUtil.c b/generic/tclUtil.c
index 869169a..278380d 100644
--- a/generic/tclUtil.c
+++ b/generic/tclUtil.c
@@ -11,10 +11,32 @@
* 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.58 2005/05/05 18:38:06 dgp Exp $
+ * RCS: @(#) $Id: tclUtil.c,v 1.59 2005/05/10 18:34:52 kennykb Exp $
*/
#include "tclInt.h"
+#include <float.h>
+#include <math.h>
+
+/*
+ * Define test for NaN
+ */
+
+#ifdef _MSC_VER
+#define IS_NAN(f) (_isnan((f)))
+#else
+#define IS_NAN(f) ((f) != (f))
+#endif
+
+/*
+ * Define test for Inf
+ */
+
+#ifdef _MSC_VER
+#define IS_INF(f) ( ! (_finite((f))))
+#else
+#define IS_INF(f) ( (f) > DBL_MAX || (f) < -DBL_MAX )
+#endif
/*
* The absolute pathname of the executable in which this Tcl library
@@ -56,12 +78,9 @@ static ProcessGlobalValue executableName = {0, 0, NULL, NULL, NULL, NULL, NULL};
* TclPrecTraceProc.
*/
-static char precisionString[10] = "12";
- /* The string value of all the tcl_precision
- * variables. */
-static char precisionFormat[10] = "%.12g";
- /* The format string actually used in calls
- * to sprintf. */
+static int precision = 0; /* Precision of floating point conversions,
+ * in the range 0-17 inclusive. */
+
TCL_DECLARE_MUTEX(precisionMutex)
/*
@@ -1879,33 +1898,125 @@ Tcl_PrintDouble(interp, value, dst)
* characters. */
{
char *p, c;
+ int prec;
+ int exp;
+ int signum;
+ char buffer[TCL_DOUBLE_SPACE];
Tcl_UniChar ch;
Tcl_MutexLock(&precisionMutex);
- sprintf(dst, precisionFormat, value);
+ prec = precision;
Tcl_MutexUnlock(&precisionMutex);
/*
- * 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.
+ * If prec == 0, then use TclDoubleDigits to develop a decimal
+ * significand and exponent, then format it in E or F format as
+ * appropriate. If prec != 0, use the native sprintf and then
+ * add a trailing ".0" if there is no decimal point in the rep.
*/
- for (p = dst; *p != 0; ) {
- if (UCHAR(*p) < 0x80) {
- c = *p++;
- } else {
- p += Tcl_UtfToUniChar(p, &ch);
- c = UCHAR(ch);
+ if ( prec == 0 ) {
+
+ /* Handle NaN */
+
+ if ( IS_NAN( value ) ) {
+ TclFormatNaN( value, dst );
+ return;
}
- if ((c == '.') || isalpha(UCHAR(c))) { /* INTL: ISO only. */
+
+ /* Handle infinities */
+
+ if ( IS_INF( value ) ) {
+ if ( value < 0 ) {
+ strcpy( dst, "-Inf" );
+ } else {
+ strcpy( dst, "Inf" );
+ }
return;
}
+
+ /* Ordinary (normal and denormal) values */
+
+ exp = TclDoubleDigits( buffer, value, &signum );
+ if ( signum ) {
+ *dst++ = '-';
+ }
+ prec = strlen( buffer );
+ p = buffer;
+ if ( exp < -3 || exp > 17 ) {
+
+ /* 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", exp-1 );
+ } else {
+
+ /* F format for others */
+
+ if ( exp <= 0 ) {
+ *dst++ = '0';
+ }
+ c = *p;
+ while ( exp-- > 0 ) {
+ if ( c != '\0' ) {
+ *dst++ = c;
+ c = *++p;
+ } else {
+ *dst++ = '0';
+ }
+ }
+ *dst++ = '.';
+ if ( c == '\0' ) {
+ *dst++ = '0';
+ } else {
+ while ( ++exp < 0 ) {
+ *dst++ = '0';
+ }
+ while ( c != '\0' ) {
+ *dst++ = c;
+ c = *++p;
+ }
+ }
+ *dst++ = '\0';
+ }
+
+ } else {
+
+ /* tcl_precision is supplied, pass it to the native sprintf */
+
+ sprintf( dst, "%.*g", prec, 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);
+ }
+ if ((c == '.') || isalpha(UCHAR(c))) { /* INTL: ISO only. */
+ return;
+ }
+ }
+ p[0] = '.';
+ p[1] = '0';
+ p[2] = 0;
+
}
- p[0] = '.';
- p[1] = '0';
- p[2] = 0;
}
/*
@@ -1937,8 +2048,7 @@ TclPrecTraceProc(clientData, interp, name1, name2, flags)
CONST char *name2; /* Second part of variable name. */
int flags; /* Information about what happened. */
{
- CONST char *value;
- char *end;
+ Tcl_Obj* value;
int prec;
/*
@@ -1961,11 +2071,11 @@ TclPrecTraceProc(clientData, interp, name1, name2, flags)
* out of date.
*/
- Tcl_MutexLock(&precisionMutex);
if (flags & TCL_TRACE_READS) {
- Tcl_SetVar2(interp, name1, name2, precisionString,
- flags & TCL_GLOBAL_ONLY);
+ Tcl_MutexLock(&precisionMutex);
+ Tcl_SetVar2Ex( interp, name1, name2, Tcl_NewIntObj( precision ),
+ flags & TCL_GLOBAL_ONLY );
Tcl_MutexUnlock(&precisionMutex);
return (char *) NULL;
}
@@ -1978,25 +2088,21 @@ TclPrecTraceProc(clientData, interp, name1, name2, flags)
*/
if (Tcl_IsSafe(interp)) {
- Tcl_SetVar2(interp, name1, name2, precisionString,
- flags & TCL_GLOBAL_ONLY);
+ Tcl_MutexLock(&precisionMutex);
+ Tcl_SetVar2Ex( interp, name1, name2, Tcl_NewIntObj( precision ),
+ flags & TCL_GLOBAL_ONLY );
Tcl_MutexUnlock(&precisionMutex);
return "can't modify precision from a safe interpreter";
}
- value = Tcl_GetVar2(interp, name1, name2, flags & TCL_GLOBAL_ONLY);
- if (value == NULL) {
- value = "";
- }
- prec = strtoul(value, &end, 10);
- if ((prec <= 0) || (prec > TCL_MAX_PREC) || (prec > 100) ||
- (end == value) || (*end != 0)) {
- Tcl_SetVar2(interp, name1, name2, precisionString,
- flags & TCL_GLOBAL_ONLY);
- Tcl_MutexUnlock(&precisionMutex);
+ value = Tcl_GetVar2Ex(interp, name1, name2, flags & TCL_GLOBAL_ONLY);
+ if ( value == NULL
+ || Tcl_GetIntFromObj( (Tcl_Interp*) NULL, value, &prec ) != TCL_OK
+ || prec < 0
+ || prec > TCL_MAX_PREC ) {
return "improper value for precision";
}
- TclFormatInt(precisionString, prec);
- sprintf(precisionFormat, "%%.%dg", prec);
+ Tcl_MutexLock( &precisionMutex );
+ precision = prec;
Tcl_MutexUnlock(&precisionMutex);
return (char *) NULL;
}