summaryrefslogtreecommitdiffstats
path: root/generic/tclStrToD.c
diff options
context:
space:
mode:
authordgp <dgp@users.sourceforge.net>2005-10-08 14:42:44 (GMT)
committerdgp <dgp@users.sourceforge.net>2005-10-08 14:42:44 (GMT)
commit76faac0f28fe9661f23ff9e35f44df1d899420e5 (patch)
tree7e3de1d0523d70328cfd81d9864b897058823d34 /generic/tclStrToD.c
parent98a6fcad96289a40b501fbd2095387a245fd804d (diff)
downloadtcl-76faac0f28fe9661f23ff9e35f44df1d899420e5.zip
tcl-76faac0f28fe9661f23ff9e35f44df1d899420e5.tar.gz
tcl-76faac0f28fe9661f23ff9e35f44df1d899420e5.tar.bz2
TIP#237 IMPLEMENTATION
[kennykb-numerics-branch] Resynchronized with the HEAD; at this checkpoint [-rkennykb-numerics-branch-20051008], the HEAD and kennykb-numerics-branch contain identical code.
Diffstat (limited to 'generic/tclStrToD.c')
-rwxr-xr-xgeneric/tclStrToD.c2562
1 files changed, 1919 insertions, 643 deletions
diff --git a/generic/tclStrToD.c b/generic/tclStrToD.c
index 68f6c30..7f8ecd5 100755
--- a/generic/tclStrToD.c
+++ b/generic/tclStrToD.c
@@ -1,21 +1,20 @@
/*
*----------------------------------------------------------------------
*
- * tclStrToD.c --
+ * tclDouble.c --
*
- * This file contains a TclStrToD procedure that handles conversion of
- * string to double, with correct rounding even where extended precision
- * is needed to achieve that. It also contains a TclDoubleDigits
- * procedure that handles conversion of double to string (at least the
- * significand), and several utility functions for interconverting
- * 'double' and the integer types.
+ * This file contains a collection of procedures for managing
+ * conversions to/from floating-point in Tcl. They include
+ * TclParseNumber, which parses numbers from strings; TclDoubleDigits,
+ * which formats numbers into strings of digits, and procedures for
+ * interconversion among 'double' and 'mp_int' types.
*
* Copyright (c) 2005 by Kevin B. Kenny. All rights reserved.
*
* 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.8 2005/08/24 15:15:45 kennykb Exp $
+ * RCS: @(#) $Id: tclStrToD.c,v 1.9 2005/10/08 14:42:45 dgp Exp $
*
*----------------------------------------------------------------------
*/
@@ -30,34 +29,42 @@
#include <tommath.h>
/*
- * The stuff below is a bit of a hack so that this file can be used in
- * environments that include no UNIX, i.e. no errno: just arrange to use the
- * errno from tclExecute.c here.
+ * Define TIP_114_FORMATS to accept 0b and 0o for binary and octal strings.
+ * Define KILL_OCTAL as well as TIP_114_FORMATS to suppress interpretation
+ * of numbers with leading zero as octal. (Ceterum censeo: numeros octonarios
+ * delendos esse.)
*/
-#ifdef TCL_GENERIC_ONLY
-# define NO_ERRNO_H
-#endif
+#define TIP_114_FORMATS
+#undef KILL_OCTAL
-#ifdef NO_ERRNO_H
-extern int errno; /* Use errno from tclExecute.c. */
-# define ERANGE 34
+#ifndef TIP_114_FORMATS
+#undef KILL_OCTAL
#endif
+/*
+ * This code supports (at least hypothetically), IBM, Cray, VAX and
+ * IEEE-754 floating point; of these, only IEEE-754 can represent NaN.
+ * IEEE-754 can be uniquely determined by radix and by the widths of
+ * significand and exponent.
+ */
+
#if (FLT_RADIX == 2) && (DBL_MANT_DIG == 53) && (DBL_MAX_EXP == 1024)
# define IEEE_FLOATING_POINT
#endif
/*
- * gcc on x86 needs access to rounding controls. It is tempting to include
- * fpu_control.h, but that file exists only on Linux; it is missing on Cygwin
- * and MinGW. Most gcc-isms and ix86-isms are factored out here.
+ * gcc on x86 needs access to rounding controls, because of a questionable
+ * feature where it retains intermediate results as IEEE 'long double' values
+ * somewhat unpredictably. It is tempting to include fpu_control.h, but
+ * that file exists only on Linux; it is missing on Cygwin and MinGW. Most
+ * gcc-isms and ix86-isms are factored out here.
*/
#if defined(__GNUC__) && defined(__i386)
typedef unsigned int fpu_control_t __attribute__ ((__mode__ (__HI__)));
-# define _FPU_GETCW(cw) __asm__ ("fnstcw %0" : "=m" (*&cw))
-# define _FPU_SETCW(cw) __asm__ ("fldcw %0" : : "m" (*&cw))
+#define _FPU_GETCW(cw) __asm__ __volatile__ ("fnstcw %0" : "=m" (*&cw))
+#define _FPU_SETCW(cw) __asm__ __volatile__ ("fldcw %0" : : "m" (*&cw))
# define FPU_IEEE_ROUNDING 0x027f
# define ADJUST_FPU_CONTROL_WORD
#endif
@@ -75,13 +82,26 @@ typedef unsigned int fpu_control_t __attribute__ ((__mode__ (__HI__)));
# define NAN_MASK (((Tcl_WideUInt) 1) << 51)
#endif
-/*
- * There now follows a lot of static variables that are shared across all
- * threads but which are not guarded by mutexes. This is OK, because they are
- * only ever assigned _ONCE_ during Tcl's library initialization sequence.
- */
+/* The powers of ten that can be represented exactly as wide integers */
+
+static int maxpow10_wide;
+static Tcl_WideUInt *pow10_wide;
+
+/* The number of decimal digits that fit in an mp_digit */
+
+static int log10_DIGIT_MAX;
+
+/* The powers of ten that can be represented exactly as IEEE754 doubles. */
+
+#define MAXPOW 22
+static double pow10 [MAXPOW+1];
+
+static int mmaxpow; /* Largest power of ten that can be
+ * represented exactly in a 'double'. */
-static const double pow_10_2_n[] = { /* Inexact higher powers of ten */
+/* Inexact higher powers of ten */
+
+static CONST double pow_10_2_n [] = {
1.0,
100.0,
10000.0,
@@ -92,440 +112,1414 @@ static const double pow_10_2_n[] = { /* Inexact higher powers of ten */
1.0e+128,
1.0e+256
};
-#define MAXPOW 22 /* Num of exactly representable powers of 10 */
-static double pow10[MAXPOW+1]; /* The powers of ten that can be represented
- * exactly as IEEE754 doubles. */
-static int mmaxpow; /* Largest power of ten that can be
- * represented exactly in a 'double'. */
-static int log2FLT_RADIX; /* Logarithm of the floating point radix. */
-static int mantBits; /* Number of bits in a double's significand. */
-static mp_int pow5[9]; /* Table of powers of 5**(2**n), up to
- * 5**256. */
-static double tiny; /* The smallest representable double. */
-static int maxDigits; /* The maximum number of digits to the left of
- * the decimal point of a double. */
-static int minDigits; /* The maximum number of digits to the right
- * of the decimal point in a double. */
-static int mantDIGIT; /* Number of mp_digit's needed to hold the
- * significand of a double. */
+
+ /* Logarithm of the floating point radix. */
+
+static int log2FLT_RADIX;
+
+/* Number of bits in a double's significand */
+
+static int mantBits;
+
+/* Table of powers of 5**(2**n), up to 5**256 */
+
+static mp_int pow5[9];
+
+/* The smallest representable double */
+
+static double tiny;
+
+/* The maximum number of digits to the left of the decimal point of a
+ * double. */
+
+static int maxDigits;
+
+/* The maximum number of digits to the right of the decimal point in a
+ * double. */
+
+static int minDigits;
+
+/* Number of mp_digit's needed to hold the significand of a double */
+
+static int mantDIGIT;
/* Static functions defined in this file */
-static double RefineResult(double approx, CONST char *start, int nDigits,
- long exponent);
-static double ParseNaN(int signum, CONST char **end);
-static double SafeLdExp(double fraction, int exponent);
+static int AccumulateDecimalDigit _ANSI_ARGS_((unsigned, int,
+ Tcl_WideUInt*, mp_int*, int));
+static double MakeLowPrecisionDouble _ANSI_ARGS_((int signum,
+ Tcl_WideUInt significand,
+ int nSigDigs,
+ int exponent));
+static double MakeHighPrecisionDouble _ANSI_ARGS_((int signum,
+ mp_int* significand,
+ int nSigDigs,
+ int exponent));
+static double MakeNaN _ANSI_ARGS_(( int signum, Tcl_WideUInt tag ));
+static double RefineApproximation _ANSI_ARGS_((double approx,
+ mp_int* exactSignificand,
+ int exponent));
+static double AbsoluteValue(double v, int* signum);
+static int GetIntegerTimesPower(double v, mp_int* r, int* e);
+static double BignumToBiasedFrExp _ANSI_ARGS_(( mp_int* big, int* machexp ));
+static double Pow10TimesFrExp _ANSI_ARGS_(( int exponent,
+ double fraction,
+ int* machexp ));
+static double SafeLdExp _ANSI_ARGS_(( double fraction, int exponent ));
+
/*
*----------------------------------------------------------------------
*
- * TclStrToD --
+ * TclParseNumber --
*
- * Scans a double from a string.
+ * Place a "numeric" internal representation on a Tcl object.
*
* Results:
- * Returns the scanned number. In the case of underflow, returns an
- * appropriately signed zero; in the case of overflow, returns an
- * appropriately signed HUGE_VAL.
+ * Returns a standard Tcl result.
*
* Side effects:
- * Stores a pointer to the end of the scanned number in '*endPtr', if
- * endPtr is not NULL. If '*endPtr' is equal to 's' on return from this
- * function, it indicates that the input string could not be recognized
- * as a number. In the case of underflow or overflow, 'errno' is set to
- * ERANGE.
+ * Stores an internal representation appropriate to the string.
+ * The internal representation may be an integer, a wide integer,
+ * a bignum, or a double.
+ *
+ * TclMakeObjNumeric is called as a common scanner in routines
+ * that expect numbers in Tcl_Obj's. It scans the string representation
+ * of a given Tcl_Obj and stores an internal rep that represents
+ * a "canonical" version of its numeric value. The value of the
+ * canonicalization is that a routine can determine simply by
+ * examining the type pointer whether an object LooksLikeInt,
+ * what size of integer is needed to hold it, and similar questions,
+ * and never needs to refer back to the string representation, even
+ * for "impure" objects.
+ *
+ * The 'strPtr' and 'endPtrPtr' arguments allow for recognizing a number
+ * that is in a substring of a Tcl_Obj, for example a screen metric or
+ * "end-" index. If 'strPtr' is not NULL, it designates where the
+ * number begins within the string. (The default is the start of
+ * objPtr's string rep, which will be constructed if necessary.)
*
- *------------------------------------------------------------------------
+ * If 'strPtr' is supplied, 'objPtr' may be NULL. In this case,
+ * no internal representation will be generated; instead, the routine
+ * will simply check for a syntactically correct number, returning
+ * TCL_OK or TCL_ERROR as appropriate, and setting *endPtrPtr if
+ * necessary.
+ *
+ * If 'endPtrPtr' is not NULL, it designates the first character
+ * after the scanned number. In this case, successfully recognizing
+ * any digits will yield a return code of TCL_OK. Only in the case
+ * where no leading string of 'strPtr' (or of objPtr's internal rep)
+ * represents a number will TCL_ERROR be returned.
+ *
+ * When only a partial string is being recognized, it is the caller's
+ * responsibility to destroy the internal representation, or at
+ * least change its type. Failure to do so will lead to subsequent
+ * problems where a string that does not represent a number will
+ * be recognized as one because it has a numeric internal representation.
+ *
+ * When the 'flags' word includes TCL_PARSE_DECIMAL_ONLY, only decimal
+ * numbers are recognized; leading 0 has no special interpretation as
+ * octal and leading '0x' is forbidden.
+ *
+ *----------------------------------------------------------------------
*/
-double
-TclStrToD(CONST char *s, /* String to scan. */
- CONST char **endPtr) /* Pointer to the end of the scanned number. */
+int
+TclParseNumber( Tcl_Interp* interp,
+ /* Tcl interpreter for error reporting.
+ * May be NULL */
+ Tcl_Obj* objPtr,
+ /* Object to receive the internal rep */
+ CONST char* type,
+ /* Type of number being parsed ("integer",
+ * "wide integer", etc. */
+ CONST char* string,
+ /* Pointer to the start of the string to
+ * scan, see above */
+ size_t length, /* Maximum length of the string to scan,
+ * see above. */
+ CONST char** endPtrPtr,
+ /* (Output) pointer to the end of the
+ * scanned number, see above */
+ int flags) /* Flags governing the parse */
{
- const char *p = s;
- const char *startOfSignificand = NULL;
- /* Start of the significand in the string. */
- int signum = 0; /* Sign of the significand. */
- double exactSignificand = 0.0;
- /* Significand, represented exactly as a
- * floating-point number. */
- int seenDigit = 0; /* Flag == 1 if a digit has been seen. */
- int nSigDigs = 0; /* Number of significant digits presented. */
- int nDigitsAfterDp = 0; /* Number of digits after the decimal point. */
- int nTrailZero = 0; /* Number of trailing zeros in the
- * significand. */
- long exponent = 0; /* Exponent. */
- int seenDp = 0; /* Flag == 1 if decimal point has been seen. */
- char c; /* One character extracted from the input. */
- volatile double v; /* Scanned value; must be 'volatile double' on
- * gc-ix86 to force correct rounding to IEEE
- * double and not Intel double-extended. */
- int machexp; /* Exponent of the machine rep of the scanned
- * value. */
- int expt2; /* Exponent for computing first approximation
- * to the true value. */
- int i, j;
- /*
- * With gcc on x86, the floating point rounding mode is double-extended.
- * This causes the result of double-precision calculations to be rounded
- * twice: once to the precision of double-extended and then again to the
- * precision of double. Double-rounding introduces gratuitous errors of
- * one ulp, so we need to change rounding mode to 53-bits.
- */
-
-#ifdef ADJUST_FPU_CONTROL_WORD
- fpu_control_t roundTo53Bits = FPU_IEEE_ROUNDING;
- fpu_control_t oldRoundingMode;
- _FPU_GETCW(oldRoundingMode);
- _FPU_SETCW(roundTo53Bits);
-# define RestoreRoundingMode() _FPU_SETCW(oldRoundingMode)
-#else
-# define RestoreRoundingMode() (void) 0 /* Do nothing */
+ enum State {
+ INITIAL, SIGNUM, ZERO, ZERO_X,
+#ifdef TIP_114_FORMATS
+ ZERO_O, ZERO_B, BINARY,
+#endif
+ HEXADECIMAL, OCTAL, BAD_OCTAL, DECIMAL,
+ LEADING_RADIX_POINT, FRACTION,
+ EXPONENT_START, EXPONENT_SIGNUM, EXPONENT,
+ sI, sIN, sINF, sINFI, sINFIN, sINFINI, sINFINIT, sINFINITY
+#ifdef IEEE_FLOATING_POINT
+ , sN, sNA, sNAN, sNANPAREN, sNANHEX, sNANFINISH
+#endif
+ } state = INITIAL;
+ enum State acceptState = INITIAL;
+
+ int signum = 0; /* Sign of the number being parsed */
+ Tcl_WideUInt significandWide = 0;
+ /* Significand of the number being
+ * parsed (if no overflow) */
+ mp_int significandBig; /* Significand of the number being
+ * parsed (if it overflows significandWide) */
+ int significandOverflow = 0;
+ /* Flag==1 iff significandBig is used */
+ Tcl_WideUInt octalSignificandWide = 0;
+ /* Significand of an octal number; needed
+ * because we don't know whether a number
+ * with a leading zero is octal or decimal
+ * until we've scanned forward to a '.' or
+ * 'e' */
+ mp_int octalSignificandBig; /* Significand of octal number once
+ * octalSignificandWide overflows */
+ int octalSignificandOverflow = 0;
+ /* Flag==1 if octalSignificandBig is used */
+ int numSigDigs = 0; /* Number of significant digits in the
+ * decimal significand */
+ int numTrailZeros = 0; /* Number of trailing zeroes at the
+ * current point in the parse. */
+ int numDigitsAfterDp = 0; /* Number of digits scanned after the
+ * decimal point */
+ int exponentSignum = 0; /* Signum of the exponent of a floating
+ * point number */
+ long exponent = 0; /* Exponent of a floating point number */
+ CONST char* p; /* Pointer to next character to scan */
+ size_t len; /* Number of characters remaining after p */
+ CONST char* acceptPoint; /* Pointer to position after last character
+ * in an acceptable number */
+ size_t acceptLen; /* Number of characters following that point */
+ int status = TCL_OK; /* Status to return to caller */
+ char d; /* Last hexadecimal digit scanned */
+ int shift = 0; /* Amount to shift when accumulating binary */
+#ifdef TIP_114_FORMATS
+ int explicitOctal = 0;
#endif
- /*
- * Discard leading whitespace from input.
+ /*
+ * Initialize string to start of the object's string rep if
+ * the caller didn't pass anything else.
*/
- while (isspace(UCHAR(*p))) {
- ++p;
+ if ( string == NULL ) {
+ string = Tcl_GetStringFromObj( objPtr, NULL );
}
- /*
- * Determine the sign of the significand.
- */
+ p = string;
+ len = length;
+ acceptPoint = p;
+ acceptLen = len;
+ while ( 1 ) {
+ char c = len ? *p : '\0';
+ switch (state) {
- switch (*p) {
- case '-':
- signum = 1;
+ case INITIAL:
+ /*
+ * Initial state. Acceptable characters are +, -, digits,
+ * period, I, N, and whitespace.
+ */
+ if (isspace(UCHAR(c))) {
+ break;
+ } else if (c == '+') {
+ state = SIGNUM;
+ break;
+ } else if (c == '-') {
+ signum = 1;
+ state = SIGNUM;
+ break;
+ }
/* FALLTHROUGH */
- case '+':
- ++p;
- }
-
- /*
- * Discard leading zeroes from input.
- */
+
+ case SIGNUM:
+ /*
+ * Scanned a leading + or -. Acceptable characters are
+ * digits, period, I, and N.
+ */
+ if (c == '0') {
+ if (flags & TCL_PARSE_DECIMAL_ONLY) {
+ state = DECIMAL;
+ } else {
+ state = ZERO;
+ }
+ break;
+ } else if (flags & TCL_PARSE_HEXADECIMAL_ONLY) {
+ goto zerox;
+ } else if (flags & TCL_PARSE_OCTAL_ONLY) {
+ goto zeroo;
+ } else if (isdigit(UCHAR(c))) {
+ significandWide = c - '0';
+ numSigDigs = 1;
+ state = DECIMAL;
+ break;
+ } else if (flags & TCL_PARSE_INTEGER_ONLY) {
+ goto endgame;
+ } else if (c == '.') {
+ state = LEADING_RADIX_POINT;
+ break;
+ } else if (c == 'I' || c == 'i') {
+ state = sI;
+ break;
+#ifdef IEEE_FLOATING_POINT
+ } else if (c == 'N' || c == 'n') {
+ state = sN;
+ break;
+#endif
+ }
+ goto endgame;
- while (*p == '0') {
- seenDigit = 1;
- ++p;
- }
+ case ZERO:
+ /*
+ * Scanned a leading zero (perhaps with a + or -).
+ * Acceptable inputs are digits, period, X, and E.
+ * If 8 or 9 is encountered, the number can't be
+ * octal. This state and the OCTAL state differ only
+ * in whether they recognize 'X'.
+ */
+ acceptState = state;
+ acceptPoint = p;
+ acceptLen = len;
+ if (c == 'x' || c == 'X') {
+ state = ZERO_X;
+ break;
+ }
+ if (flags & TCL_PARSE_HEXADECIMAL_ONLY) {
+ goto zerox;
+ }
+#ifdef TIP_114_FORMATS
+ if (flags & TCL_PARSE_SCAN_PREFIXES) {
+ goto zeroo;
+ }
+ if (c == 'b' || c == 'B') {
+ state = ZERO_B;
+ break;
+ }
+ if (c == 'o' || c == 'O') {
+ explicitOctal = 1;
+ state = ZERO_O;
+ break;
+ }
+#ifdef KILL_OCTAL
+ goto decimal;
+#endif
+#endif
+ /* FALLTHROUGH */
- /*
- * Scan digits from the significand. Simultaneously, keep track of the
- * number of digits after the decimal point. Maintain a pointer to the
- * start of the significand. Keep "exactSignificand" equal to the
- * conversion of the DBL_DIG most significant digits.
- */
+ case OCTAL:
+ /*
+ * Scanned an optional + or -, followed by a string of
+ * octal digits. Acceptable inputs are more digits,
+ * period, or E. If 8 or 9 is encountered, commit to
+ * floating point.
+ */
+ acceptState = state;
+ acceptPoint = p;
+ acceptLen = len;
+#ifdef TIP_114_FORMATS
+ /* FALLTHROUGH */
+ case ZERO_O:
+#endif
+ zeroo:
+ if (c == '0') {
+ ++numTrailZeros;
+ state = OCTAL;
+ break;
+ } else if (c >= '1' && c <= '7') {
+ if (objPtr != NULL) {
+ shift = 3 * (numTrailZeros + 1);
+ significandOverflow =
+ AccumulateDecimalDigit((unsigned)(c-'0'),
+ numTrailZeros,
+ &significandWide,
+ &significandBig,
+ significandOverflow);
+
+ if (!octalSignificandOverflow) {
+ /*
+ * Shifting by more bits than are in the value being
+ * shifted is at least de facto nonportable. Check
+ * for too large shifts first.
+ */
+ if ((octalSignificandWide != 0)
+ && ((shift >= CHAR_BIT*sizeof(Tcl_WideUInt))
+ || (octalSignificandWide
+ > (~(Tcl_WideUInt)0 >> shift)))) {
+ octalSignificandOverflow = 1;
+ TclBNInitBignumFromWideUInt(&octalSignificandBig,
+ octalSignificandWide);
+ }
+ }
+ if (!octalSignificandOverflow) {
+ octalSignificandWide
+ = (octalSignificandWide << shift) + (c - '0');
+ } else {
+ mp_mul_2d(&octalSignificandBig, shift,
+ &octalSignificandBig);
+ mp_add_d(&octalSignificandBig, (mp_digit)(c - '0'),
+ &octalSignificandBig);
+ }
+ }
+ if ( numSigDigs != 0 ) {
+ numSigDigs += ( numTrailZeros + 1 );
+ } else {
+ numSigDigs = 1;
+ }
+ numTrailZeros = 0;
+ state = OCTAL;
+ break;
+ }
+ /* FALLTHROUGH */
- for (;;) {
- c = *p;
- if (c == '.' && !seenDp) {
- seenDp = 1;
- ++p;
- } else if (isdigit(UCHAR(c))) {
+ case BAD_OCTAL:
+#ifdef TIP_114_FORMATS
+ if (explicitOctal) {
+ /* No forgiveness for bad digits in explicitly octal numbers */
+ goto endgame;
+ }
+#endif
+ if (flags & TCL_PARSE_INTEGER_ONLY) {
+ /* No seeking floating point when parsing only integer */
+ goto endgame;
+ }
+#ifndef KILL_OCTAL
+ /*
+ * Scanned a number with a leading zero that contains an
+ * 8, 9, radix point or E. This is an invalid octal number,
+ * but might still be floating point.
+ */
if (c == '0') {
- if (startOfSignificand != NULL) {
- ++nTrailZero;
+ ++numTrailZeros;
+ state = BAD_OCTAL;
+ break;
+ } else if (isdigit(UCHAR(c))) {
+ if (objPtr != NULL) {
+ significandOverflow =
+ AccumulateDecimalDigit((unsigned)(c-'0'),
+ numTrailZeros,
+ &significandWide,
+ &significandBig,
+ significandOverflow);
+ }
+ if ( numSigDigs != 0 ) {
+ numSigDigs += ( numTrailZeros + 1 );
+ } else {
+ numSigDigs = 1;
}
+ numTrailZeros = 0;
+ state = BAD_OCTAL;
+ break;
+ } else if (c == '.') {
+ state = FRACTION;
+ break;
+ } else if (c == 'E' || c == 'e') {
+ state = EXPONENT_START;
+ break;
+ }
+#endif
+ goto endgame;
+
+ /*
+ * Scanned 0x. If state is HEXADECIMAL, scanned at least
+ * one character following the 0x. The only acceptable
+ * inputs are hexadecimal digits.
+ */
+ case HEXADECIMAL:
+ acceptState = state;
+ acceptPoint = p;
+ acceptLen = len;
+ /* FALLTHROUGH */
+ case ZERO_X:
+ zerox:
+ if (c == '0') {
+ ++numTrailZeros;
+ state = HEXADECIMAL;
+ break;
+ } else if (isdigit(UCHAR(c))) {
+ d = (c-'0');
+ } else if (c >= 'A' && c <= 'F') {
+ d = (c-'A'+10);
+ } else if (c >= 'a' && c <= 'f') {
+ d = (c-'a'+10);
} else {
- if (startOfSignificand == NULL) {
- startOfSignificand = p;
- } else if (nTrailZero) {
- if (nTrailZero + nSigDigs < DBL_DIG) {
- exactSignificand *= pow10[nTrailZero];
- } else if (nSigDigs < DBL_DIG) {
- exactSignificand *= pow10[DBL_DIG - nSigDigs];
+ goto endgame;
+ }
+ if (objPtr != NULL) {
+ shift = 4 * (numTrailZeros + 1);
+ if (!significandOverflow) {
+ /*
+ * Shifting by more bits than are in the value being
+ * shifted is at least de facto nonportable. Check
+ * for too large shifts first.
+ */
+ if (significandWide != 0
+ && (shift >= CHAR_BIT*sizeof(Tcl_WideUInt)
+ || significandWide > (~(Tcl_WideUInt)0 >> shift))) {
+ significandOverflow = 1;
+ TclBNInitBignumFromWideUInt(&significandBig,
+ significandWide);
}
- nSigDigs += nTrailZero;
}
- if (nSigDigs < DBL_DIG) {
- exactSignificand = 10. * exactSignificand + (c - '0');
+ if (!significandOverflow) {
+ significandWide
+ = (significandWide << shift) + d;
+ } else {
+ mp_mul_2d(&significandBig, shift,
+ &significandBig);
+ mp_add_d(&significandBig, (mp_digit) d,
+ &significandBig);
}
- ++nSigDigs;
- nTrailZero = 0;
}
- if (seenDp) {
- ++nDigitsAfterDp;
+ numTrailZeros = 0;
+ state = HEXADECIMAL;
+ break;
+
+#ifdef TIP_114_FORMATS
+ case BINARY:
+ acceptState = state;
+ acceptPoint = p;
+ acceptLen = len;
+ case ZERO_B:
+ if (c == '0') {
+ ++numTrailZeros;
+ state = BINARY;
+ break;
+ } else if (c != '1') {
+ goto endgame;
}
- seenDigit = 1;
- ++p;
- } else {
+ if (objPtr != NULL) {
+ shift = numTrailZeros + 1;
+ if (!significandOverflow) {
+ /*
+ * Shifting by more bits than are in the value being
+ * shifted is at least de facto nonportable. Check
+ * for too large shifts first.
+ */
+ if (significandWide != 0
+ && (shift >= CHAR_BIT*sizeof(Tcl_WideUInt)
+ || significandWide > (~(Tcl_WideUInt)0 >> shift))) {
+ significandOverflow = 1;
+ TclBNInitBignumFromWideUInt(&significandBig,
+ significandWide);
+ }
+ }
+ if (!significandOverflow) {
+ significandWide
+ = (significandWide << shift) + 1;
+ } else {
+ mp_mul_2d(&significandBig, shift,
+ &significandBig);
+ mp_add_d(&significandBig, (mp_digit) 1,
+ &significandBig);
+ }
+ }
+ numTrailZeros = 0;
+ state = BINARY;
break;
- }
- }
+#endif
- /*
- * At this point, we've scanned the significand, and p points to the
- * character beyond it. "startOfSignificand" is the first non-zero
- * character in the significand. "nSigDigs" is the number of significant
- * digits of the significand, not including any trailing zeroes.
- * "exactSignificand" is a floating point number that represents, without
- * loss of precision, the first min(DBL_DIG,n) digits of the significand.
- * "nDigitsAfterDp" is the number of digits after the decimal point, again
- * excluding trailing zeroes.
- *
- * Now scan 'E' format
- */
+ case DECIMAL:
+ /*
+ * Scanned an optional + or - followed by a string of
+ * decimal digits.
+ */
+#ifdef KILL_OCTAL
+ decimal:
+#endif
+ acceptState = state;
+ acceptPoint = p;
+ acceptLen = len;
+ if (c == '0') {
+ ++numTrailZeros;
+ state = DECIMAL;
+ break;
+ } else if (isdigit(UCHAR(c))) {
+ if (objPtr != NULL) {
+ significandOverflow =
+ AccumulateDecimalDigit((unsigned)(c - '0'),
+ numTrailZeros,
+ &significandWide,
+ &significandBig,
+ significandOverflow);
+ }
+ numSigDigs += ( numTrailZeros + 1 );
+ numTrailZeros = 0;
+ state = DECIMAL;
+ break;
+ } else if (flags & TCL_PARSE_INTEGER_ONLY) {
+ goto endgame;
+ } else if (c == '.') {
+ state = FRACTION;
+ break;
+ } else if (c == 'E' || c == 'e') {
+ state = EXPONENT_START;
+ break;
+ }
+ goto endgame;
- exponent = 0;
- if (seenDigit && (*p == 'e' || *p == 'E')) {
- const char* stringSave = p;
- ++p;
- c = *p;
- if (isdigit(UCHAR(c)) || c == '+' || c == '-') {
- errno = 0;
- exponent = strtol(p, (char**)&p, 10);
- if (errno == ERANGE) {
- if (exponent > 0) {
- v = HUGE_VAL;
+ /*
+ * Found a decimal point. If no digits have yet been scanned,
+ * E is not allowed; otherwise, it introduces the exponent.
+ * If at least one digit has been found, we have a possible
+ * complete number.
+ */
+ case FRACTION:
+ acceptState = state;
+ acceptPoint = p;
+ acceptLen = len;
+ if (c == 'E' || c=='e') {
+ state = EXPONENT_START;
+ break;
+ }
+ /* FALLTHROUGH */
+ case LEADING_RADIX_POINT:
+ if (c == '0') {
+ ++numDigitsAfterDp;
+ ++numTrailZeros;
+ state = FRACTION;
+ break;
+ } else if (isdigit(UCHAR(c))) {
+ ++numDigitsAfterDp;
+ if (objPtr != NULL) {
+ significandOverflow =
+ AccumulateDecimalDigit((unsigned)(c-'0'),
+ numTrailZeros,
+ &significandWide,
+ &significandBig,
+ significandOverflow);
+ }
+ if ( numSigDigs != 0 ) {
+ numSigDigs += ( numTrailZeros + 1 );
} else {
- v = 0.0;
+ numSigDigs = 1;
}
- *endPtr = p;
- goto returnValue;
+ numTrailZeros = 0;
+ state = FRACTION;
+ break;
}
- }
- if (p == stringSave+1) {
- p = stringSave;
- exponent = 0;
- }
- }
- exponent += nTrailZero - nDigitsAfterDp;
+ goto endgame;
- /*
- * If we come here with no significant digits, we might still be looking
- * at Inf or NaN. Go parse them.
- */
+ case EXPONENT_START:
+ /*
+ * Scanned the E at the start of an exponent. Make sure
+ * a legal character follows before using the C library
+ * strtol routine, which allows whitespace.
+ */
+ if (c == '+') {
+ state = EXPONENT_SIGNUM;
+ break;
+ } else if (c == '-') {
+ exponentSignum = 1;
+ state = EXPONENT_SIGNUM;
+ break;
+ }
+ /* FALLTHROUGH */
- if (!seenDigit) {
- /*
- * Test for Inf or Infinity (in any case).
- */
+ case EXPONENT_SIGNUM:
+ /*
+ * Found the E at the start of the exponent, followed by
+ * a sign character.
+ */
+ if (isdigit(UCHAR(c))) {
+ exponent = c - '0';
+ state = EXPONENT;
+ break;
+ }
+ goto endgame;
- if (c == 'I' || c == 'i') {
- if ((p[1] == 'N' || p[1] == 'n')
- && (p[2] == 'F' || p[2] == 'f')) {
- p += 3;
- if ((p[0] == 'I' || p[0] == 'i')
- && (p[1] == 'N' || p[1] == 'n')
- && (p[2] == 'I' || p[2] == 'i')
- && (p[3] == 'T' || p[3] == 't')
- && (p[4] == 'Y' || p[1] == 'y')) {
- p += 5;
- }
- errno = ERANGE;
- v = HUGE_VAL;
- if (endPtr != NULL) {
- *endPtr = p;
+ case EXPONENT:
+ /*
+ * Found an exponent with at least one digit.
+ * Accumulate it, making sure to hard-pin it to LONG_MAX
+ * on overflow.
+ */
+ acceptState = state;
+ acceptPoint = p;
+ acceptLen = len;
+ if (isdigit(UCHAR(c))) {
+ if (exponent < (LONG_MAX - 9) / 10) {
+ exponent = 10 * exponent + (c - '0');
+ } else {
+ exponent = LONG_MAX;
}
- goto returnValue;
+ state = EXPONENT;
+ break;
}
+ goto endgame;
-#ifdef IEEE_FLOATING_POINT
/*
- * Only IEEE floating point supports NaN
+ * Parse out INFINITY by simply spelling it out.
+ * INF is accepted as an abbreviation; other prefices are
+ * not.
*/
- } else if ((c == 'N' || c == 'n')
- && (sizeof(Tcl_WideUInt) == sizeof(double))) {
- if ((p[1] == 'A' || p[1] == 'a')
- && (p[2] == 'N' || p[2] == 'n')) {
- p += 3;
-
- if (endPtr != NULL) {
- *endPtr = p;
- }
+ case sI:
+ if ( c == 'n' || c == 'N' ) {
+ state = sIN;
+ break;
+ }
+ goto endgame;
+ case sIN:
+ if ( c == 'f' || c == 'F' ) {
+ state = sINF;
+ break;
+ }
+ goto endgame;
+ case sINF:
+ acceptState = state;
+ acceptPoint = p;
+ acceptLen = len;
+ if ( c == 'i' || c == 'I' ) {
+ state = sINFI;
+ break;
+ }
+ goto endgame;
+ case sINFI:
+ if ( c == 'n' || c == 'N' ) {
+ state = sINFIN;
+ break;
+ }
+ goto endgame;
+ case sINFIN:
+ if ( c == 'i' || c == 'I' ) {
+ state = sINFINI;
+ break;
+ }
+ goto endgame;
+ case sINFINI:
+ if ( c == 't' || c == 'T' ) {
+ state = sINFINIT;
+ break;
+ }
+ goto endgame;
+ case sINFINIT:
+ if ( c == 'y' || c == 'Y' ) {
+ state = sINFINITY;
+ break;
+ }
+ goto endgame;
- /*
- * Restore FPU mode word.
- */
+ /*
+ * Parse NaN's.
+ */
+#ifdef IEEE_FLOATING_POINT
+ case sN:
+ if ( c == 'a' || c == 'A' ) {
+ state = sNA;
+ break;
+ }
+ goto endgame;
+ case sNA:
+ if ( c == 'n' || c == 'N' ) {
+ state = sNAN;
+ break;
+ }
+ case sNAN:
+ acceptState = state;
+ acceptPoint = p;
+ acceptLen = len;
+ if ( c == '(' ) {
+ state = sNANPAREN;
+ break;
+ }
+ goto endgame;
- RestoreRoundingMode();
- return ParseNaN(signum, endPtr);
+ /*
+ * Parse NaN(hexdigits)
+ */
+ case sNANHEX:
+ if ( c == ')' ) {
+ state = sNANFINISH;
+ break;
}
+ /* FALLTHROUGH */
+ case sNANPAREN:
+ if ( isspace(UCHAR(c)) ) {
+ break;
+ }
+ if ( numSigDigs < 13 ) {
+ if ( c >= '0' && c <= '9' ) {
+ d = c - '0';
+ } else if ( c >= 'a' && c <= 'f' ) {
+ d = 10 + c - 'a';
+ } else if ( c >= 'A' && c <= 'F' ) {
+ d = 10 + c - 'A';
+ }
+ significandWide = (significandWide << 4) + d;
+ state = sNANHEX;
+ break;
+ }
+ goto endgame;
+ case sNANFINISH:
#endif
+ case sINFINITY:
+ acceptState = state;
+ acceptPoint = p;
+ acceptLen = len;
+ goto endgame;
}
+ ++p;
+ --len;
+ }
+
+ endgame:
- goto error;
+ /* Back up to the last accepting state in the lexer */
+
+ if (acceptState == INITIAL) {
+ status = TCL_ERROR;
}
+ p = acceptPoint;
+ len = acceptLen;
- /*
- * We've successfully scanned; update the end-of-element pointer.
- */
+ /* Skip past trailing whitespace */
- if (endPtr != NULL) {
- *endPtr = p;
+ if (endPtrPtr != NULL) {
+ *endPtrPtr = p;
}
- /*
- * Test for zero.
- */
+ while (len > 0 && isspace(UCHAR(*p))) {
+ ++p;
+ --len;
+ }
- if (nSigDigs == 0) {
- v = 0.0;
- goto returnValue;
+ /* Determine whether a partial string is acceptable. */
+
+ if (endPtrPtr == NULL && len != 0 && *p != '\0') {
+ status = TCL_ERROR;
}
- /*
- * The easy cases are where we have an exact significand and the exponent
- * is small enough that we can compute the value with only one roundoff.
- * In addition to the cases where we can multiply or divide an
- * exact-integer significand by an exact-integer power of 10, there is
- * also David Gay's case where we can scale the significand by a power of
- * 10 (still keeping it exact) and then multiply by an exact power of 10.
- * The last case enables combinations like 83e25 that would otherwise
- * require high precision arithmetic.
- */
+ /* Generate and store the appropriate internal rep */
- if (nSigDigs <= DBL_DIG) {
- if (exponent >= 0) {
- if (exponent <= mmaxpow) {
- v = exactSignificand * pow10[exponent];
- goto returnValue;
- } else {
- int diff = DBL_DIG - nSigDigs;
- if (exponent - diff <= mmaxpow) {
- volatile double factor = exactSignificand * pow10[diff];
- v = factor * pow10[exponent - diff];
- goto returnValue;
+ if (status == TCL_OK && objPtr != NULL) {
+ if ( acceptState != INITIAL ) {
+ TclFreeIntRep( objPtr );
+ }
+ switch (acceptState) {
+
+ case INITIAL:
+ status = TCL_ERROR;
+ break;
+
+ case SIGNUM:
+ case BAD_OCTAL:
+ case ZERO_X:
+#ifdef TIP_114_FORMATS
+ case ZERO_O:
+ case ZERO_B:
+#endif
+ case LEADING_RADIX_POINT:
+ case EXPONENT_START:
+ case EXPONENT_SIGNUM:
+ case sI:
+ case sIN:
+ case sINFI:
+ case sINFIN:
+ case sINFINI:
+ case sINFINIT:
+ case sN:
+ case sNA:
+ case sNANPAREN:
+ case sNANHEX:
+ panic("in TclParseNumber: bad acceptState, can't happen.");
+
+#ifdef TIP_114_FORMATS
+ case BINARY:
+ shift = numTrailZeros;
+ if (!significandOverflow) {
+ if (significandWide !=0
+ && (shift >= CHAR_BIT*sizeof(Tcl_WideUInt)
+ || significandWide
+ > (((~(Tcl_WideUInt)0) >> 1) + signum) >> shift )) {
+ significandOverflow = 1;
+ TclBNInitBignumFromWideUInt(&significandBig,
+ significandWide);
}
}
- } else if (exponent >= -mmaxpow) {
- v = exactSignificand / pow10[-exponent];
- goto returnValue;
+ if (shift) {
+ if ( !significandOverflow ) {
+ significandWide <<= shift;
+ } else {
+ mp_mul_2d( &significandBig, shift, &significandBig );
+ }
+ }
+ goto returnInteger;
+#endif
+ case HEXADECIMAL:
+ /* Returning a hex integer. Final scaling step */
+ shift = 4 * numTrailZeros;
+ if (!significandOverflow) {
+ if (significandWide !=0
+ && (shift >= CHAR_BIT*sizeof(Tcl_WideUInt)
+ || significandWide
+ > (((~(Tcl_WideUInt)0) >> 1) + signum) >> shift )) {
+ significandOverflow = 1;
+ TclBNInitBignumFromWideUInt(&significandBig,
+ significandWide);
+ }
+ }
+ if (shift) {
+ if ( !significandOverflow ) {
+ significandWide <<= shift;
+ } else {
+ mp_mul_2d( &significandBig, shift, &significandBig );
+ }
+ }
+ goto returnInteger;
+
+ case OCTAL:
+ /* Returning an octal integer. Final scaling step */
+ shift = 3 * numTrailZeros;
+ if (!octalSignificandOverflow) {
+ if (octalSignificandWide != 0
+ && (shift >= CHAR_BIT*sizeof(Tcl_WideUInt)
+ || octalSignificandWide
+ > (((~(Tcl_WideUInt)0) >> 1) + signum) >> shift )) {
+ octalSignificandOverflow = 1;
+ TclBNInitBignumFromWideUInt(&octalSignificandBig,
+ octalSignificandWide);
+ }
+ }
+ if ( shift ) {
+ if ( !octalSignificandOverflow ) {
+ octalSignificandWide <<= shift;
+ } else {
+ mp_mul_2d( &octalSignificandBig, shift,
+ &octalSignificandBig );
+ }
+ }
+ if (!octalSignificandOverflow) {
+ if (octalSignificandWide >
+ (Tcl_WideUInt)(((~(unsigned long)0) >> 1) + signum)) {
+#ifndef NO_WIDE_TYPE
+ if (octalSignificandWide
+ <= (((~(Tcl_WideUInt)0) >> 1) + signum)) {
+ objPtr->typePtr = &tclWideIntType;
+ if (signum) {
+ objPtr->internalRep.wideValue =
+ - (Tcl_WideInt) octalSignificandWide;
+ } else {
+ objPtr->internalRep.wideValue =
+ (Tcl_WideInt) octalSignificandWide;
+ }
+ break;
+ }
+#endif
+ TclBNInitBignumFromWideUInt(&octalSignificandBig,
+ octalSignificandWide);
+ octalSignificandOverflow = 1;
+ } else {
+ objPtr->typePtr = &tclIntType;
+ if (signum) {
+ objPtr->internalRep.longValue =
+ - (long) octalSignificandWide;
+ } else {
+ objPtr->internalRep.longValue =
+ (long) octalSignificandWide;
+ }
+ }
+ }
+ if (octalSignificandOverflow) {
+ if (signum) {
+ mp_neg(&octalSignificandBig, &octalSignificandBig);
+ }
+ TclSetBignumIntRep(objPtr, &octalSignificandBig);
+ }
+ break;
+
+ case ZERO:
+ case DECIMAL:
+ significandOverflow =
+ AccumulateDecimalDigit( 0, numTrailZeros-1,
+ &significandWide, &significandBig,
+ significandOverflow );
+ if (!significandOverflow
+ && (significandWide
+ > (((~(Tcl_WideUInt)0) >> 1) + signum))) {
+ significandOverflow = 1;
+ TclBNInitBignumFromWideUInt(&significandBig,
+ significandWide);
+ }
+ returnInteger:
+ if (!significandOverflow) {
+ if (significandWide >
+ (Tcl_WideUInt)(((~(unsigned long)0) >> 1) + signum)) {
+#ifndef NO_WIDE_TYPE
+ if (significandWide
+ <= (((~(Tcl_WideUInt)0) >> 1) + signum)) {
+ objPtr->typePtr = &tclWideIntType;
+ if (signum) {
+ objPtr->internalRep.wideValue =
+ - (Tcl_WideInt) significandWide;
+ } else {
+ objPtr->internalRep.wideValue =
+ (Tcl_WideInt) significandWide;
+ }
+ break;
+ }
+#endif
+ TclBNInitBignumFromWideUInt(&significandBig,
+ significandWide);
+ significandOverflow = 1;
+ } else {
+ objPtr->typePtr = &tclIntType;
+ if (signum) {
+ objPtr->internalRep.longValue =
+ - (long) significandWide;
+ } else {
+ objPtr->internalRep.longValue =
+ (long) significandWide;
+ }
+ }
+ }
+ if (significandOverflow) {
+ if (signum) {
+ mp_neg(&significandBig, &significandBig);
+ }
+ TclSetBignumIntRep(objPtr, &significandBig);
+ }
+ break;
+
+ case FRACTION:
+ case EXPONENT:
+
+ /*
+ * Here, we're parsing a floating-point number.
+ * 'significandWide' or 'significandBig' contains the
+ * exact significand, according to whether
+ * 'significandOverflow' is set. The desired floating
+ * point value is significand * 10**k, where
+ * k = numTrailZeros+exponent-numDigitsAfterDp.
+ */
+
+ objPtr->typePtr = &tclDoubleType;
+ if ( exponentSignum ) {
+ exponent = - exponent;
+ }
+ if ( !significandOverflow ) {
+ objPtr->internalRep.doubleValue =
+ MakeLowPrecisionDouble( signum,
+ significandWide,
+ numSigDigs,
+ ( numTrailZeros
+ + exponent
+ - numDigitsAfterDp ) );
+ } else {
+ objPtr->internalRep.doubleValue =
+ MakeHighPrecisionDouble( signum,
+ &significandBig,
+ numSigDigs,
+ ( numTrailZeros
+ + exponent
+ - numDigitsAfterDp ) );
+ }
+ break;
+
+ case sINF:
+ case sINFINITY:
+ if ( signum ) {
+ objPtr->internalRep.doubleValue = -HUGE_VAL;
+ } else {
+ objPtr->internalRep.doubleValue = HUGE_VAL;
+ }
+ objPtr->typePtr = &tclDoubleType;
+ break;
+
+ case sNAN:
+ case sNANFINISH:
+ objPtr->internalRep.doubleValue
+ = MakeNaN( signum, significandWide );
+ objPtr->typePtr = &tclDoubleType;
+ break;
+
}
}
- /*
- * We don't have one of the easy cases, so we can't compute the scanned
- * number exactly, and have to do it in multiple precision. Begin by
- * testing for obvious overflows and underflows.
- */
+ /* Format an error message when an invalid number is encountered. */
+
+ if ( status != TCL_OK ) {
+ if ( interp != NULL ) {
+ Tcl_Obj *msg = Tcl_NewStringObj( "expected ", -1 );
+ Tcl_AppendToObj( msg, type, -1 );
+ Tcl_AppendToObj( msg, " but got \"", -1 );
+ TclAppendLimitedToObj( msg, string, length, 50, "" );
+ Tcl_AppendToObj( msg, "\"", -1 );
+ if ( state == BAD_OCTAL ) {
+ Tcl_AppendToObj( msg, " (looks like invalid octal number)",
+ -1 );
+ }
+ Tcl_SetObjResult( interp, msg );
+ }
+ }
- if (nSigDigs + exponent - 1 > maxDigits) {
- v = HUGE_VAL;
- errno = ERANGE;
- goto returnValue;
+ /* Free memory */
+
+ if (octalSignificandOverflow) {
+ mp_clear(&octalSignificandBig);
}
- if (nSigDigs + exponent - 1 < minDigits) {
- errno = ERANGE;
- v = 0.;
- goto returnValue;
+ if (significandOverflow) {
+ mp_clear(&significandBig);
}
+ return status;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * AccumulateDecimalDigit --
+ *
+ * Consume a decimal digit in a number being scanned.
+ *
+ * Results:
+ * Returns 1 if the number has overflowed to a bignum, 0 if it
+ * still fits in a wide integer.
+ *
+ * Side effects:
+ * Updates either the wide or bignum representation.
+ *
+ *----------------------------------------------------------------------
+ */
- /*
- * Nothing exceeds the boundaries of the tables, at least. Compute an
- * approximate value for the number, with no possibility of overflow
- * because we manage the exponent separately.
- */
-
- if (nSigDigs > DBL_DIG) {
- expt2 = exponent + nSigDigs - DBL_DIG;
- } else {
- expt2 = exponent;
- }
- v = frexp(exactSignificand, &machexp);
- if (expt2 > 0) {
- v = frexp(v * pow10[expt2 & 0xf], &j);
- machexp += j;
- for (i=4 ; i<9 ; ++i) {
- if (expt2 & (1 << i)) {
- v = frexp(v * pow_10_2_n[i], &j);
- machexp += j;
+static int
+AccumulateDecimalDigit( unsigned digit,
+ /* Digit being scanned */
+ int numZeros,
+ /* Count of zero digits preceding the
+ * digit being scanned */
+ Tcl_WideUInt* wideRepPtr,
+ /* Representation of the partial number
+ * as a wide integer */
+ mp_int* bignumRepPtr,
+ /* Representation of the partial number
+ * as a bignum */
+ int bignumFlag )
+ /* Flag == 1 if the number overflowed
+ * previous to this digit. */
+{
+ int i, n;
+
+ /* Check if the number still fits in a wide */
+
+ if (!bignumFlag) {
+ if (*wideRepPtr != 0) {
+ if ((numZeros >= maxpow10_wide)
+ || (*wideRepPtr > (((~(Tcl_WideUInt)0) - digit)
+ / pow10_wide[numZeros+1]))) {
+ /* Oops, it's overflowed, have to allocate a bignum */
+ TclBNInitBignumFromWideUInt (bignumRepPtr, *wideRepPtr);
+ bignumFlag = 1;
}
}
+ }
+
+ /* Multiply the number by 10**numZeros+1 and add in the new digit. */
+
+ if (!bignumFlag) {
+
+ /* Wide multiplication */
+
+ *wideRepPtr = *wideRepPtr * pow10_wide[numZeros+1] + digit;
+ } else if (numZeros < log10_DIGIT_MAX ) {
+
+ /* Up to about 8 zeros - single digit multiplication */
+
+ mp_mul_d (bignumRepPtr, (mp_digit) pow10_wide[numZeros+1],
+ bignumRepPtr);
+ mp_add_d (bignumRepPtr, (mp_digit) digit, bignumRepPtr);
+
} else {
- v = frexp(v / pow10[(-expt2) & 0xf], &j);
- machexp += j;
- for (i=4 ; i<9 ; ++i) {
- if ((-expt2) & (1 << i)) {
- v = frexp(v / pow_10_2_n[i], &j);
- machexp += j;
+
+ /*
+ * More than single digit multiplication. Multiply by the appropriate
+ * small powers of 5, and then shift. Large strings of zeroes are
+ * eaten 256 at a time; this is less efficient than it could be,
+ * but seems implausible. We presume that DIGIT_BIT is at least 27.
+ * The first multiplication, by up to 10**7, is done with a
+ * one-DIGIT multiply (this presumes that DIGIT_BIT >= 24).
+ */
+
+ n = numZeros + 1;
+ mp_mul_d (bignumRepPtr, (mp_digit) pow10_wide[n&0x7], bignumRepPtr);
+ for (i = 3; i <= 7; ++i) {
+ if (n & (1 << i)) {
+ mp_mul (bignumRepPtr, pow5+i, bignumRepPtr);
}
}
+ while (n >= 256) {
+ mp_mul (bignumRepPtr, pow5+8, bignumRepPtr);
+ n -= 256;
+ }
+ mp_mul_2d (bignumRepPtr, (int)(numZeros+1)&~0x7, bignumRepPtr);
}
+ return bignumFlag;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * MakeLowPrecisionDouble --
+ *
+ * Makes the double precision number, signum*significand*10**exponent.
+ *
+ * Results:
+ * Returns the constructed number.
+ *
+ * Common cases, where there are few enough digits that the number can
+ * be represented with at most roundoff, are handled specially here.
+ * If the number requires more than one rounded operation to compute,
+ * the code promotes the significand to a bignum and calls
+ * MakeHighPrecisionDouble to do it instead.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static double
+MakeLowPrecisionDouble( int signum,
+ /* 1 if the number is negative, 0 otherwise */
+ Tcl_WideUInt significand,
+ /* Significand of the number */
+ int numSigDigs,
+ /* Number of digits in the significand */
+ int exponent )
+ /* Power of ten */
+{
+ double retval; /* Value of the number */
+ mp_int significandBig; /* Significand expressed as a bignum */
+
/*
- * A first approximation is that the result will be v * 2 ** machexp. v is
- * greater than or equal to 0.5 and less than 1. If machexp >
- * DBL_MAX_EXP*log2(FLT_RADIX), there is an overflow. Constrain the result
- * to the smallest representible number to avoid premature underflow.
+ * With gcc on x86, the floating point rounding mode is double-extended.
+ * This causes the result of double-precision calculations to be rounded
+ * twice: once to the precision of double-extended and then again to the
+ * precision of double. Double-rounding introduces gratuitous errors of
+ * 1 ulp, so we need to change rounding mode to 53-bits.
*/
- if (machexp > DBL_MAX_EXP * log2FLT_RADIX) {
- v = HUGE_VAL;
- errno = ERANGE;
- goto returnValue;
- }
+#if defined(__GNUC__) && defined(__i386)
+ fpu_control_t roundTo53Bits = 0x027f;
+ fpu_control_t oldRoundingMode;
+ _FPU_GETCW( oldRoundingMode );
+ _FPU_SETCW( roundTo53Bits );
+#endif
- v = SafeLdExp(v, machexp);
- if (v < tiny) {
- v = tiny;
- }
+ /* Test for the easy cases */
- /*
- * We have a first approximation in v. Now we need to refine it.
- */
+ if ( numSigDigs <= DBL_DIG ) {
+ if ( exponent >= 0 ) {
+ if ( exponent <= mmaxpow ) {
- v = RefineResult(v, startOfSignificand, nSigDigs, exponent);
+ /*
+ * The significand is an exact integer, and so is
+ * 10**exponent. The product will be correct to within
+ * 1/2 ulp without special handling.
+ */
- /*
- * In a very few cases, a second iteration is needed. e.g., 457e-102
- */
+ retval = (double)(Tcl_WideInt)significand * pow10[ exponent ];
+ goto returnValue;
- v = RefineResult(v, startOfSignificand, nSigDigs, exponent);
+ } else {
+ int diff = DBL_DIG - numSigDigs;
+ if ( exponent-diff <= mmaxpow ) {
+
+ /*
+ * 10**exponent is not an exact integer, but
+ * 10**(exponent-diff) is exact, and so is
+ * significand*10**diff, so we can still compute
+ * the value with only one roundoff.
+ */
+ volatile double factor
+ = (double)(Tcl_WideInt)significand * pow10[diff];
+ retval = factor * pow10[exponent-diff];
+ goto returnValue;
+ }
+ }
+ } else {
+ if ( exponent >= -mmaxpow ) {
+
+ /*
+ * 10**-exponent is an exact integer, and so is the
+ * significand. Compute the result by one division,
+ * again with only one rounding.
+ */
+
+ retval = (double)(Tcl_WideInt)significand / pow10[-exponent];
+ goto returnValue;
+ }
+ }
+ }
/*
- * Handle underflow.
+ * All the easy cases have failed. Promote ths significand
+ * to bignum and call MakeHighPrecisionDouble to do it the hard way.
*/
+ TclBNInitBignumFromWideUInt (&significandBig, significand);
+ retval = MakeHighPrecisionDouble( 0, &significandBig, numSigDigs,
+ exponent );
+
+ /* Come here to return the computed value */
+
returnValue:
- if (nSigDigs != 0 && v == 0.0) {
- errno = ERANGE;
+
+ if ( signum ) {
+ retval = -retval;
}
+ /* On gcc on x86, restore the floating point mode word. */
+
+#if defined(__GNUC__) && defined(__i386)
+ _FPU_SETCW( oldRoundingMode );
+#endif
+
+ return retval;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * MakeHighPrecisionDouble --
+ *
+ * Makes the double precision number, signum*significand*10**exponent.
+ *
+ * Results:
+ * Returns the constructed number.
+ *
+ * MakeHighPrecisionDouble is used when arbitrary-precision arithmetic
+ * is needed to ensure correct rounding. It begins by calculating a
+ * low-precision approximation to the desired number, and then refines
+ * the answer in high precision.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static double
+MakeHighPrecisionDouble( int signum,
+ /* 1=negative, 0=nonnegative */
+ mp_int* significand,
+ /* Exact significand of the number */
+ int numSigDigs,
+ /* Number of significant digits */
+ int exponent )
+ /* Power of 10 by which to multiply */
+{
+
+ double retval;
+ int machexp; /* Machine exponent of a power of 10 */
+
/*
- * Return a number with correct sign.
+ * With gcc on x86, the floating point rounding mode is double-extended.
+ * This causes the result of double-precision calculations to be rounded
+ * twice: once to the precision of double-extended and then again to the
+ * precision of double. Double-rounding introduces gratuitous errors of
+ * 1 ulp, so we need to change rounding mode to 53-bits.
*/
- if (signum) {
- v = -v;
+#if defined(__GNUC__) && defined(__i386)
+ fpu_control_t roundTo53Bits = 0x027f;
+ fpu_control_t oldRoundingMode;
+ _FPU_GETCW( oldRoundingMode );
+ _FPU_SETCW( roundTo53Bits );
+#endif
+
+ /* Quick checks for over/underflow */
+
+ if ( numSigDigs + exponent - 1 > maxDigits ) {
+ retval = HUGE_VAL;
+ goto returnValue;
+ }
+ if ( numSigDigs + exponent - 1 < minDigits ) {
+ retval = 0;
+ goto returnValue;
}
- /*
- * Restore FPU mode word and return.
+ /*
+ * Develop a first approximation to the significand. It is tempting
+ * simply to force bignum to double, but that will overflow on input
+ * numbers like 1.[string repeat 0 1000]1; while this is a not terribly
+ * likely scenario, we still have to deal with it. Use fraction and
+ * exponent instead. Once we have the significand, multiply by
+ * 10**exponent. Test for overflow. Convert back to a double, and
+ * test for underflow.
*/
- RestoreRoundingMode();
- return v;
+ retval = BignumToBiasedFrExp( significand, &machexp );
+ retval = Pow10TimesFrExp( exponent, retval, &machexp );
+ if ( machexp > DBL_MAX_EXP * log2FLT_RADIX ) {
+ retval = HUGE_VAL;
+ goto returnValue;
+ }
+ retval = SafeLdExp( retval, machexp );
+ if ( retval < tiny ) {
+ retval = tiny;
+ }
- /*
- * Come here on an invalid input.
+ /*
+ * Refine the result twice. (The second refinement should be
+ * necessary only if the best approximation is a power of 2
+ * minus 1/2 ulp).
*/
- error:
- if (endPtr != NULL) {
- *endPtr = s;
+ retval = RefineApproximation( retval, significand, exponent );
+ retval = RefineApproximation( retval, significand, exponent );
+
+ /* Come here to return the computed value */
+
+ returnValue:
+ if ( signum ) {
+ retval = -retval;
}
- /*
- * Restore FPU mode word and return.
- */
+ /* On gcc on x86, restore the floating point mode word. */
- RestoreRoundingMode();
- return 0.0;
+#if defined(__GNUC__) && defined(__i386)
+ _FPU_SETCW( oldRoundingMode );
+#endif
+ return retval;
}
/*
*----------------------------------------------------------------------
*
- * RefineResult --
+ * MakeNaN --
+ *
+ * Makes a "Not a Number" given a set of bits to put in the
+ * tag bits
*
- * Given a poor approximation to a floating point number, returns a
- * better one. (The better approximation is correct to within 1 ulp, and
- * is entirely correct if the poor approximation is correct to 1 ulp.)
+ * Note that a signalling NaN is never returned.
+ *
+ *----------------------------------------------------------------------
+ */
+
+#ifdef IEEE_FLOATING_POINT
+static double
+MakeNaN( int signum, /* Sign bit (1=negative, 0=nonnegative */
+ Tcl_WideUInt tags ) /* Tag bits to put in the NaN */
+{
+ union {
+ Tcl_WideUInt iv;
+ double dv;
+ } theNaN;
+
+ theNaN.iv = tags;
+ theNaN.iv &= ( ((Tcl_WideUInt) 1) << 51 ) - 1;
+ if ( signum ) {
+ theNaN.iv |= ((Tcl_WideUInt) (0x8000 | NAN_START)) << 48;
+ } else {
+ theNaN.iv |= ((Tcl_WideUInt) NAN_START) << 48;
+ }
+
+ return theNaN.dv;
+}
+#endif
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * RefineApproximation --
+ *
+ * Given a poor approximation to a floating point number, returns
+ * a better one (The better approximation is correct to within
+ * 1 ulp, and is entirely correct if the poor approximation is
+ * correct to 1 ulp.)
*
* Results:
* Returns the improved result.
@@ -534,119 +1528,109 @@ TclStrToD(CONST char *s, /* String to scan. */
*/
static double
-RefineResult(double approxResult, /* Approximate result of conversion. */
- CONST char* sigStart,
- /* Pointer to start of significand in input
- * string. */
- int nSigDigs, /* Number of significant digits. */
- long exponent) /* Power of ten to multiply by significand. */
+RefineApproximation( double approxResult,
+ /* Approximate result of conversion */
+ mp_int* exactSignificand,
+ /* Integer significand */
+ int exponent )
+ /* Power of 10 to multiply by significand */
{
- int M2, M5; /* Powers of 2 and of 5 needed to put the
- * decimal and binary numbers over a common
- * denominator. */
- double significand; /* Sigificand of the binary number. */
- int binExponent; /* Exponent of the binary number. */
+
+ int M2, M5; /* Powers of 2 and of 5 needed to put
+ * the decimal and binary numbers over
+ * a common denominator. */
+ double significand; /* Sigificand of the binary number */
+ int binExponent; /* Exponent of the binary number */
+
int msb; /* Most significant bit position of an
- * intermediate result. */
+ * intermediate result */
int nDigits; /* Number of mp_digit's in an intermediate
- * result. */
- mp_int twoMv; /* Approx binary value expressed as an exact
- * integer scaled by the multiplier 2M. */
- mp_int twoMd; /* Exact decimal value expressed as an exact
- * integer scaled by the multiplier 2M. */
- int scale; /* Scale factor for M. */
- int multiplier; /* Power of two to scale M. */
- double num, den; /* Numerator and denominator of the correction
- * term. */
- double quot; /* Correction term. */
- double minincr; /* Lower bound on the absolute value of the
- * correction term. */
+ * result */
+ mp_int twoMv; /* Approx binary value expressed as an
+ * exact integer scaled by the multiplier 2M */
+ mp_int twoMd; /* Exact decimal value expressed as an
+ * exact integer scaled by the multiplier 2M */
+ int scale; /* Scale factor for M */
+ int multiplier; /* Power of two to scale M */
+ double num, den; /* Numerator and denominator of the
+ * correction term */
+ double quot; /* Correction term */
+ double minincr; /* Lower bound on the absolute value
+ * of the correction term. */
int i;
- const char* p;
/*
- * The first approximation is always low. If we find that it's HUGE_VAL,
- * we're done.
+ * The first approximation is always low. If we find that
+ * it's HUGE_VAL, we're done.
*/
- if (approxResult == HUGE_VAL) {
+ if ( approxResult == HUGE_VAL ) {
return approxResult;
}
/*
- * Find a common denominator for the decimal and binary fractions. The
- * common denominator will be 2**M2 + 5**M5.
+ * Find a common denominator for the decimal and binary fractions.
+ * The common denominator will be 2**M2 + 5**M5.
*/
- significand = frexp(approxResult, &binExponent);
+ significand = frexp( approxResult, &binExponent );
i = mantBits - binExponent;
- if (i < 0) {
+ if ( i < 0 ) {
M2 = 0;
} else {
M2 = i;
}
- if (exponent > 0) {
+ if ( exponent > 0 ) {
M5 = 0;
} else {
M5 = -exponent;
- if ((M5-1) > M2) {
+ if ( (M5-1) > M2 ) {
M2 = M5-1;
}
}
- /*
- * The floating point number is significand*2**binExponent. The 2**-1 bit
- * of the significand (the most significant) corresponds to the
- * 2**(binExponent+M2 + 1) bit of 2*M2*v. Allocate enough digits to hold
- * that quantity, then convert the significand to a large integer, scaled
+ /*
+ * The floating point number is significand*2**binExponent.
+ * Compute the large integer significand*2**(binExponent+M2+1)
+ * The 2**-1 bit of the significand (the most significant)
+ * corresponds to the 2**(binExponent+M2 + 1) bit of 2*M2*v.
+ * Allocate enough digits to hold that quantity, then
+ * convert the significand to a large integer, scaled
* appropriately. Then multiply by the appropriate power of 5.
*/
- msb = binExponent + M2; /* 1008 */
+ msb = binExponent + M2; /* 1008 */
nDigits = msb / DIGIT_BIT + 1;
- mp_init_size(&twoMv, nDigits);
- i = (msb % DIGIT_BIT + 1);
+ mp_init_size( &twoMv, nDigits );
+ i = ( msb % DIGIT_BIT + 1 );
twoMv.used = nDigits;
- significand *= SafeLdExp(1.0, i);
- while (--nDigits >= 0) {
+ significand *= SafeLdExp( 1.0, i );
+ while ( -- nDigits >= 0 ) {
twoMv.dp[nDigits] = (mp_digit) significand;
significand -= (mp_digit) significand;
- significand = SafeLdExp(significand, DIGIT_BIT);
+ significand = SafeLdExp( significand, DIGIT_BIT );
}
- for (i=0 ; i<=8 ; ++i) {
- if (M5 & (1 << i)) {
- mp_mul(&twoMv, pow5+i, &twoMv);
+ for ( i = 0; i <= 8; ++i ) {
+ if ( M5 & ( 1 << i ) ) {
+ mp_mul( &twoMv, pow5+i, &twoMv );
}
}
-
- /*
- * Collect the decimal significand as a high precision integer. The least
- * significant bit corresponds to bit M2+exponent+1 so it will need to be
- * shifted left by that many bits after being multiplied by
- * 5**(M5+exponent).
+
+ /*
+ * Collect the decimal significand as a high precision integer.
+ * The least significant bit corresponds to bit M2+exponent+1
+ * so it will need to be shifted left by that many bits after
+ * being multiplied by 5**(M5+exponent).
*/
- mp_init(&twoMd);
- mp_zero(&twoMd);
- i = nSigDigs;
- for (p=sigStart ;; ++p) {
- char c = *p;
- if (isdigit(UCHAR(c))) {
- mp_mul_d(&twoMd, (unsigned) 10, &twoMd);
- mp_add_d(&twoMd, (unsigned) (c - '0'), &twoMd);
- --i;
- if (i == 0) {
- break;
- }
- }
- }
- for (i=0 ; i<=8 ; ++i) {
- if ((M5+exponent) & (1 << i)) {
- mp_mul(&twoMd, pow5+i, &twoMd);
+ mp_init_copy( &twoMd, exactSignificand );
+ for ( i = 0; i <= 8; ++i ) {
+ if ( (M5+exponent) & ( 1 << i ) ) {
+ mp_mul( &twoMd, pow5+i, &twoMd );
}
}
- mp_mul_2d(&twoMd, M2+exponent+1, &twoMd);
- mp_sub(&twoMd, &twoMv, &twoMd);
+ mp_mul_2d( &twoMd, M2+exponent+1, &twoMd );
+ mp_sub( &twoMd, &twoMv, &twoMd );
/*
* The result, 2Mv-2Md, needs to be divided by 2M to yield a correction
@@ -656,135 +1640,55 @@ RefineResult(double approxResult, /* Approximate result of conversion. */
scale = binExponent - mantBits - 1;
- mp_set(&twoMv, 1);
- for (i=0 ; i<=8 ; ++i) {
- if (M5 & (1 << i)) {
- mp_mul(&twoMv, pow5+i, &twoMv);
+ mp_set( &twoMv, 1 );
+ for ( i = 0; i <= 8; ++i ) {
+ if ( M5 & ( 1 << i ) ) {
+ mp_mul( &twoMv, pow5+i, &twoMv );
}
}
multiplier = M2 + scale + 1;
- if (multiplier > 0) {
- mp_mul_2d(&twoMv, multiplier, &twoMv);
- } else if (multiplier < 0) {
- mp_div_2d(&twoMv, -multiplier, &twoMv, NULL);
+ if ( multiplier > 0 ) {
+ mp_mul_2d( &twoMv, multiplier, &twoMv );
+ } else if ( multiplier < 0 ) {
+ mp_div_2d( &twoMv, -multiplier, &twoMv, NULL );
}
/*
- * If the result is less than unity, the error is less than 1/2 unit in
- * the last place, so there's no correction to make.
+ * If the result is less than unity, the error is less than 1/2 unit
+ * in the last place, so there's no correction to make.
*/
- if (mp_cmp_mag(&twoMd, &twoMv) == MP_LT) {
- mp_clear(&twoMd);
- mp_clear(&twoMv);
+ if ( mp_cmp_mag( &twoMd, &twoMv ) == MP_LT ) {
return approxResult;
}
- /*
- * Convert the numerator and denominator of the corrector term accurately
- * to floating point numbers.
+ /*
+ * Convert the numerator and denominator of the corrector term
+ * accurately to floating point numbers.
*/
- num = TclBignumToDouble(&twoMd);
- den = TclBignumToDouble(&twoMv);
+ num = TclBignumToDouble( &twoMd );
+ den = TclBignumToDouble( &twoMv );
- quot = SafeLdExp(num/den, scale);
- minincr = SafeLdExp(1.0, binExponent - mantBits);
+ quot = SafeLdExp( num/den, scale );
+ minincr = SafeLdExp( 1.0, binExponent - mantBits );
- if (quot<0. && quot>-minincr) {
+ if ( quot < 0. && quot > -minincr ) {
quot = -minincr;
- } else if (quot>0. && quot<minincr) {
+ } else if ( quot > 0. && quot < minincr ) {
quot = minincr;
}
- mp_clear(&twoMd);
- mp_clear(&twoMv);
+ mp_clear( &twoMd );
+ mp_clear( &twoMv );
+
return approxResult + quot;
}
/*
*----------------------------------------------------------------------
*
- * ParseNaN --
- *
- * Parses a "not a number" from an input string, and returns the double
- * precision NaN corresponding to it.
- *
- * Side effects:
- * Advances endPtr to follow any (hex) in the input string.
- *
- * If the NaN is followed by a left paren, a string of spaes and
- * hexadecimal digits, and a right paren, endPtr is advanced to follow
- * it.
- *
- * The string of hexadecimal digits is OR'ed into the resulting NaN, and
- * the signum is set as well. Note that a signalling NaN is never
- * returned.
- *
- *----------------------------------------------------------------------
- */
-
-static double
-ParseNaN(int signum, /* Flag == 1 if minus sign has been seen in
- * front of NaN. */
- CONST char** endPtr) /* Pointer-to-pointer to char following "NaN"
- * in the input string. */
-{
- const char* p = *endPtr;
- char c;
- union {
- Tcl_WideUInt iv;
- double dv;
- } theNaN;
-
- /*
- * Scan off a hex number in parentheses. Embedded blanks are ok.
- */
-
- theNaN.iv = 0;
- if (*p == '(') {
- ++p;
- for (;;) {
- c = *p++;
- if (isspace(UCHAR(c))) {
- continue;
- } else if (c == ')') {
- *endPtr = p;
- break;
- } else if (isdigit(UCHAR(c))) {
- c -= '0';
- } else if (c >= 'A' && c <= 'F') {
- c -= 'A' + 10;
- } else if (c >= 'a' && c <= 'f') {
- c -= 'a' + 10;
- } else {
- theNaN.iv = (((Tcl_WideUInt) NAN_START) << 48)
- | (((Tcl_WideUInt) signum) << 63);
- return theNaN.dv;
- }
- theNaN.iv = (theNaN.iv << 4) | c;
- }
- }
-
- /*
- * Mask the hex number down to the least significant 51 bits.
- */
-
- theNaN.iv &= (((Tcl_WideUInt) 1) << 51) - 1;
- if (signum) {
- theNaN.iv |= ((Tcl_WideUInt) 0xfff8) << 48;
- } else {
- theNaN.iv |= ((Tcl_WideUInt) NAN_START) << 48;
- }
-
- *endPtr = p;
- return theNaN.dv;
-}
-
-/*
- *----------------------------------------------------------------------
- *
* TclDoubleDigits --
*
* Converts a double to a string of digits.
@@ -803,20 +1707,21 @@ ParseNaN(int signum, /* Flag == 1 if minus sign has been seen in
*/
int
-TclDoubleDigits(char * strPtr, /* Buffer in which to store the result, must
- * have at least 18 chars. */
- double v, /* Number to convert. Must be finite, and not
- * NaN. */
- int *signum) /* Output: 1 if the number is negative.
- * Should handle -0 correctly on the IEEE
- * architecture. */
+TclDoubleDigits( char * string, /* Buffer in which to store the result,
+ * must have at least 18 chars */
+ double v, /* Number to convert. Must be
+ * finite, and not NaN */
+ int *signum ) /* Output: 1 if the number is negative.
+ * Should handle -0 correctly on the
+ * IEEE architecture. */
{
- double f; /* Significand of v. */
int e; /* Power of FLT_RADIX that satisfies
* v = f * FLT_RADIX**e */
int lowOK, highOK;
mp_int r; /* Scaled significand. */
mp_int s; /* Divisor such that v = r / s */
+ int smallestSig; /* Flag == 1 iff v's significand is
+ * the smallest that can be represented. */
mp_int mplus; /* Scaled epsilon: (r + 2* mplus) == v(+)
* where v(+) is the floating point successor
* of v. */
@@ -830,103 +1735,31 @@ TclDoubleDigits(char * strPtr, /* Buffer in which to store the result, must
int sfac5 = 0;
int mplusfac2 = 0;
int mminusfac2 = 0;
- double a;
char c;
int i, k, n;
- /*
- * Take the absolute value of the number, and report the number's sign.
- * Take special steps to preserve signed zeroes in IEEE floating point.
- * (We can't use fpclassify, because that's a C9x feature and we still
- * have to build on C89 compilers.)
- */
+ /* Split the number into absolute value and signum. */
-#ifndef IEEE_FLOATING_POINT
- if (v >= 0.0) {
- *signum = 0;
- } else {
- *signum = 1;
- v = -v;
- }
-#else
- union {
- Tcl_WideUInt iv;
- double dv;
- } bitwhack;
- bitwhack.dv = v;
- if (bitwhack.iv & ((Tcl_WideUInt) 1 << 63)) {
- *signum = 1;
- bitwhack.iv &= ~((Tcl_WideUInt) 1 << 63);
- v = bitwhack.dv;
- } else {
- *signum = 0;
- }
-#endif
+ v = AbsoluteValue(v, signum);
/*
* Handle zero specially.
*/
- if (v == 0.0) {
- *strPtr++ = '0';
- *strPtr++ = '\0';
+ if ( v == 0.0 ) {
+ *string++ = '0';
+ *string++ = '\0';
return 1;
}
- /*
- * Develop f and e such that v = f * FLT_RADIX**e, with
- * 1.0/FLT_RADIX <= f < 1.
+ /*
+ * Find a large integer r, and integer e, such that
+ * v = r * FLT_RADIX**e
+ * and r is as small as possible. Also determine whether the
+ * significand is the smallest possible.
*/
- f = frexp(v, &e);
- n = e % log2FLT_RADIX;
- if (n > 0) {
- n -= log2FLT_RADIX;
- e += 1;
- }
- f *= ldexp(1.0, n);
- e = (e - n) / log2FLT_RADIX;
- if (f == 1.0) {
- f = 1.0 / FLT_RADIX;
- e += 1;
- }
-
- /*
- * If the original number was denormalized, adjust e and f to be denormal
- * as well.
- */
-
- if (e < DBL_MIN_EXP) {
- n = mantBits + (e - DBL_MIN_EXP)*log2FLT_RADIX;
- f = ldexp(f, (e - DBL_MIN_EXP)*log2FLT_RADIX);
- e = DBL_MIN_EXP;
- n = (n + DIGIT_BIT - 1) / DIGIT_BIT;
- } else {
- n = mantDIGIT;
- }
-
- /*
- * Now extract the base-2**DIGIT_BIT digits of f into a multi-precision
- * integer r. Preserve the invariant v = r * 2**rfac2 * FLT_RADIX**e by
- * adjusting e.
- */
-
- a = f;
- n = mantDIGIT;
- mp_init_size(&r, n);
- r.used = n;
- r.sign = MP_ZPOS;
- i = (mantBits % DIGIT_BIT);
- if (i == 0) {
- i = DIGIT_BIT;
- }
- while (n > 0) {
- a *= ldexp(1.0, i);
- i = DIGIT_BIT;
- r.dp[--n] = (mp_digit) a;
- a -= (mp_digit) a;
- }
- e -= DBL_MANT_DIG;
+ smallestSig = GetIntegerTimesPower(v, &r, &e);
lowOK = highOK = (mp_iseven(&r));
@@ -943,12 +1776,12 @@ TclDoubleDigits(char * strPtr, /* Buffer in which to store the result, must
if (e >= 0) {
int bits = e * log2FLT_RADIX;
- if (f != 1.0/FLT_RADIX) {
+ if (!smallestSig) {
/*
* Normal case, m+ and m- are both FLT_RADIX**e
*/
- rfac2 += bits + 1;
+ rfac2 = bits + 1;
sfac2 = 1;
mplusfac2 = bits;
mminusfac2 = bits;
@@ -959,7 +1792,7 @@ TclDoubleDigits(char * strPtr, /* Buffer in which to store the result, must
* smaller exponent when going to e's predecessor.
*/
- rfac2 += bits + log2FLT_RADIX + 1;
+ rfac2 = bits + log2FLT_RADIX + 1;
sfac2 = 1 + log2FLT_RADIX;
mplusfac2 = bits + log2FLT_RADIX;
mminusfac2 = bits;
@@ -969,13 +1802,13 @@ TclDoubleDigits(char * strPtr, /* Buffer in which to store the result, must
* v has digits after the binary point
*/
- if (e <= DBL_MIN_EXP-DBL_MANT_DIG || f != 1.0/FLT_RADIX) {
+ if (e <= DBL_MIN_EXP-DBL_MANT_DIG || !smallestSig) {
/*
* Either f isn't the smallest significand or e is the smallest
* exponent. mplus and mminus will both be 1.
*/
- rfac2 += 1;
+ rfac2 = 1;
sfac2 = 1 - e * log2FLT_RADIX;
mplusfac2 = 0;
mminusfac2 = 0;
@@ -986,7 +1819,7 @@ TclDoubleDigits(char * strPtr, /* Buffer in which to store the result, must
* fact that v's predecessor has a smaller exponent.
*/
- rfac2 += 1 + log2FLT_RADIX;
+ rfac2 = 1 + log2FLT_RADIX;
sfac2 = 1 + log2FLT_RADIX * (1 - e);
mplusfac2 = FLT_RADIX;
mminusfac2 = 0;
@@ -1081,9 +1914,9 @@ TclDoubleDigits(char * strPtr, /* Buffer in which to store the result, must
} else {
tc2= (tc2 > 0);
}
- if (!tc1) {
- if (!tc2) {
- *strPtr++ = '0' + i;
+ if ( ! tc1 ) {
+ if ( !tc2 ) {
+ *string++ = '0' + i;
} else {
c = (char) (i + '1');
break;
@@ -1103,8 +1936,8 @@ TclDoubleDigits(char * strPtr, /* Buffer in which to store the result, must
break;
}
};
- *strPtr++ = c;
- *strPtr++ = '\0';
+ *string++ = c;
+ *string++ = '\0';
/*
* Free memory, and return.
@@ -1117,6 +1950,148 @@ TclDoubleDigits(char * strPtr, /* Buffer in which to store the result, must
/*
*----------------------------------------------------------------------
*
+ * AbsoluteValue --
+ *
+ * Splits a 'double' into its absolute value and sign.
+ *
+ * Results:
+ * Returns the absolute value.
+ *
+ * Side effects:
+ * Stores the signum in '*signum'.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static double
+AbsoluteValue (double v, /* Number to split */
+ int* signum) /* (Output) Sign of the number 1=-, 0=+ */
+{
+ /*
+ * Take the absolute value of the number, and report the number's sign.
+ * Take special steps to preserve signed zeroes in IEEE floating point.
+ * (We can't use fpclassify, because that's a C9x feature and we still
+ * have to build on C89 compilers.)
+ */
+
+#ifndef IEEE_FLOATING_POINT
+ if (v >= 0.0) {
+ *signum = 0;
+ } else {
+ *signum = 1;
+ v = -v;
+ }
+#else
+ union {
+ Tcl_WideUInt iv;
+ double dv;
+ } bitwhack;
+ bitwhack.dv = v;
+ if (bitwhack.iv & ((Tcl_WideUInt) 1 << 63)) {
+ *signum = 1;
+ bitwhack.iv &= ~((Tcl_WideUInt) 1 << 63);
+ v = bitwhack.dv;
+ } else {
+ *signum = 0;
+ }
+#endif
+ return v;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetIntegerTimesPower --
+ *
+ * Converts a floating point number to an exact integer times a
+ * power of the floating point radix.
+ *
+ * Results:
+ * Returns 1 if it converted the smallest significand, 0 otherwise.
+ *
+ * Side effects:
+ * Initializes the integer value (does not just assign it),
+ * and stores the exponent.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+GetIntegerTimesPower(double v, /* Value to convert */
+ mp_int* rPtr,
+ /* (Output) Integer value */
+ int* ePtr) /* (Output) Power of FLT_RADIX by which
+ * r must be multiplied to yield v*/
+{
+
+ double a;
+ double f;
+ int e;
+ int i;
+ int n;
+
+ /*
+ * Develop f and e such that v = f * FLT_RADIX**e, with
+ * 1.0/FLT_RADIX <= f < 1.
+ */
+
+ f = frexp(v, &e);
+#if FLT_RADIX > 2
+ n = e % log2FLT_RADIX;
+ if (n > 0) {
+ n -= log2FLT_RADIX;
+ e += 1;
+ f *= ldexp(1.0, n);
+ }
+ e = (e - n) / log2FLT_RADIX;
+#endif
+ if (f == 1.0) {
+ f = 1.0 / FLT_RADIX;
+ e += 1;
+ }
+
+ /*
+ * If the original number was denormalized, adjust e and f to be denormal
+ * as well.
+ */
+
+ if (e < DBL_MIN_EXP) {
+ n = mantBits + (e - DBL_MIN_EXP)*log2FLT_RADIX;
+ f = ldexp(f, (e - DBL_MIN_EXP)*log2FLT_RADIX);
+ e = DBL_MIN_EXP;
+ n = (n + DIGIT_BIT - 1) / DIGIT_BIT;
+ } else {
+ n = mantDIGIT;
+ }
+
+ /*
+ * Now extract the base-2**DIGIT_BIT digits of f into a multi-precision
+ * integer r. Preserve the invariant v = r * 2**rfac2 * FLT_RADIX**e by
+ * adjusting e.
+ */
+
+ a = f;
+ n = mantDIGIT;
+ mp_init_size(rPtr, n);
+ rPtr->used = n;
+ rPtr->sign = MP_ZPOS;
+ i = (mantBits % DIGIT_BIT);
+ if (i == 0) {
+ i = DIGIT_BIT;
+ }
+ while (n > 0) {
+ a *= ldexp(1.0, i);
+ i = DIGIT_BIT;
+ rPtr->dp[--n] = (mp_digit) a;
+ a -= (mp_digit) a;
+ }
+ *ePtr = e - DBL_MANT_DIG;
+ return (f == 1.0 / FLT_RADIX);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
* TclInitDoubleConversion --
*
* Initializes constants that are needed for conversions to and from
@@ -1138,16 +2113,43 @@ TclInitDoubleConversion(void)
{
int i;
int x;
+ Tcl_WideUInt u;
double d;
- if (frexp((double) FLT_RADIX, &log2FLT_RADIX) != 0.5) {
- Tcl_Panic("This code doesn't work on a decimal machine!");
+ /*
+ * Initialize table of powers of 10 expressed as wide integers.
+ */
+
+ maxpow10_wide =
+ (int) floor(sizeof (Tcl_WideUInt) * CHAR_BIT * log (2.) / log (10.));
+ pow10_wide = (Tcl_WideUInt*) Tcl_Alloc ((maxpow10_wide + 1)
+ * sizeof (Tcl_WideUInt));
+ u = 1;
+ for (i = 0; i < maxpow10_wide; ++i) {
+ pow10_wide[i] = u;
+ u *= 10;
+ }
+ pow10_wide[i] = u;
+
+ /*
+ * Determine how many bits of precision a double has, and how many
+ * decimal digits that represents.
+ */
+
+ if ( frexp( (double) FLT_RADIX, &log2FLT_RADIX ) != 0.5 ) {
+ Tcl_Panic( "This code doesn't work on a decimal machine!" );
}
--log2FLT_RADIX;
mantBits = DBL_MANT_DIG * log2FLT_RADIX;
d = 1.0;
- x = (int) (DBL_MANT_DIG * log((double) FLT_RADIX) / log(5.0));
- if (x < MAXPOW) {
+
+ /*
+ * Initialize a table of powers of ten that can be exactly represented
+ * in a double.
+ */
+
+ x = (int) (DBL_MANT_DIG * log((double) FLT_RADIX) / log( 5.0 ));
+ if ( x < MAXPOW ) {
mmaxpow = x;
} else {
mmaxpow = MAXPOW;
@@ -1156,19 +2158,32 @@ TclInitDoubleConversion(void)
pow10[i] = d;
d *= 10.0;
}
- for (i=0 ; i<9 ; ++i) {
- mp_init(pow5 + i);
+
+ /* Initialize a table of large powers of five. */
+
+ for ( i = 0; i < 9; ++i ) {
+ mp_init( pow5 + i );
}
- mp_set(pow5, 5);
- for (i=0 ; i<8 ; ++i) {
- mp_sqr(pow5+i, pow5+i+1);
+ mp_set( pow5, 5 );
+ for ( i = 0; i < 8; ++i ) {
+ mp_sqr( pow5+i, pow5+i+1 );
}
- tiny = SafeLdExp(1.0, DBL_MIN_EXP * log2FLT_RADIX - mantBits);
- maxDigits = (int)
- ((DBL_MAX_EXP * log((double) FLT_RADIX) + log(10.)/2) / log(10.));
- minDigits = (int)
- floor((DBL_MIN_EXP-DBL_MANT_DIG)*log((double)FLT_RADIX)/log(10.));
- mantDIGIT = (mantBits + DIGIT_BIT - 1) / DIGIT_BIT;
+
+ /*
+ * Determine the number of decimal digits to the left and right of the
+ * decimal point in the largest and smallest double, the smallest double
+ * that differs from zero, and the number of mp_digits needed to represent
+ * the significand of a double.
+ */
+
+ tiny = SafeLdExp( 1.0, DBL_MIN_EXP * log2FLT_RADIX - mantBits );
+ maxDigits = (int) ((DBL_MAX_EXP * log((double) FLT_RADIX)
+ + 0.5 * log(10.))
+ / log( 10. ));
+ minDigits = (int) floor ( ( DBL_MIN_EXP - DBL_MANT_DIG )
+ * log( (double) FLT_RADIX ) / log( 10. ) );
+ mantDIGIT = ( mantBits + DIGIT_BIT - 1 ) / DIGIT_BIT;
+ log10_DIGIT_MAX = (int) floor (DIGIT_BIT * log(2.) / log (10.));
}
/*
@@ -1191,9 +2206,62 @@ void
TclFinalizeDoubleConversion()
{
int i;
- for (i=0 ; i<9 ; ++i) {
- mp_clear(pow5 + i);
+ Tcl_Free ((char*)pow10_wide);
+ for ( i = 0; i < 9; ++i ) {
+ mp_clear( pow5 + i );
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclInitBignumFromDouble --
+ *
+ * Extracts the integer part of a double and converts it to
+ * an arbitrary precision integer.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Initializes the bignum supplied, and stores the converted number
+ * in it.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TclInitBignumFromDouble(Tcl_Interp *interp, /* For error message */
+ double d, /* Number to convert */
+ mp_int* b) /* Place to store the result */
+{
+ double fract;
+ int expt;
+
+ /* Infinite values can't convert to bignum */
+ if (TclIsInfinite(d)) {
+ if (interp != NULL) {
+ char *s = "integer value too large to represent";
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(s, -1));
+ Tcl_SetErrorCode(interp, "ARITH", "IOVERFLOW", s, NULL);
+ }
+ return TCL_ERROR;
+ }
+ fract = frexp(d,&expt);
+ if (expt <= 0) {
+ mp_init(b);
+ mp_zero(b);
+ } else {
+ Tcl_WideInt w = (Tcl_WideInt) ldexp(fract, mantBits);
+ int shift = expt - mantBits;
+ TclBNInitBignumFromWideInt(b, w);
+ if (shift < 0) {
+ mp_div_2d(b, -shift, b, NULL);
+ } else if (shift > 0) {
+ mp_mul_2d(b, shift, b);
+ }
}
+ return TCL_OK;
}
/*
@@ -1228,7 +2296,11 @@ TclBignumToDouble(mp_int *a) /* Integer to convert. */
bits = mp_count_bits(a);
if (bits > DBL_MAX_EXP*log2FLT_RADIX) {
errno = ERANGE;
- return HUGE_VAL;
+ if (a->sign == MP_ZPOS) {
+ return HUGE_VAL;
+ } else {
+ return -HUGE_VAL;
+ }
}
shift = mantBits + 1 - bits;
mp_init(&b);
@@ -1268,6 +2340,210 @@ TclBignumToDouble(mp_int *a) /* Integer to convert. */
return -r;
}
}
+
+double
+TclCeil(mp_int *a) /* Integer to convert. */
+{
+ double r = 0.0;
+ mp_int b;
+
+ mp_init(&b);
+ if (mp_cmp_d(a, 0) == MP_LT) {
+ mp_neg(a, &b);
+ r = -TclFloor(&b);
+ } else {
+ int bits = mp_count_bits(a);
+
+ if (bits > DBL_MAX_EXP*log2FLT_RADIX) {
+ r = HUGE_VAL;
+ } else {
+ int i, exact = 1, shift = mantBits - bits;
+
+ if (shift > 0) {
+ mp_mul_2d(a, shift, &b);
+ } else if (shift < 0) {
+ mp_int d;
+ mp_init(&d);
+ mp_div_2d(a, -shift, &b, &d);
+ exact = mp_iszero(&d);
+ mp_clear(&d);
+ } else {
+ mp_copy(a, &b);
+ }
+ if (!exact) {
+ mp_add_d(&b, 1, &b);
+ }
+ for (i=b.used-1 ; i>=0 ; --i) {
+ r = ldexp(r, DIGIT_BIT) + b.dp[i];
+ }
+ r = ldexp(r, bits - mantBits);
+ }
+ }
+ mp_clear(&b);
+ return r;
+}
+
+double
+TclFloor(mp_int *a) /* Integer to convert. */
+{
+ double r = 0.0;
+ mp_int b;
+
+ mp_init(&b);
+ if (mp_cmp_d(a, 0) == MP_LT) {
+ mp_neg(a, &b);
+ r = -TclCeil(&b);
+ } else {
+ int bits = mp_count_bits(a);
+
+ if (bits > DBL_MAX_EXP*log2FLT_RADIX) {
+ r = DBL_MAX;
+ } else {
+ int i, shift = mantBits - bits;
+
+ if (shift > 0) {
+ mp_mul_2d(a, shift, &b);
+ } else if (shift < 0) {
+ mp_div_2d(a, -shift, &b, NULL);
+ } else {
+ mp_copy(a, &b);
+ }
+ for (i=b.used-1 ; i>=0 ; --i) {
+ r = ldexp(r, DIGIT_BIT) + b.dp[i];
+ }
+ r = ldexp(r, bits - mantBits);
+ }
+ }
+ mp_clear(&b);
+ return r;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * BignumToBiasedFrExp --
+ *
+ * Convert an arbitrary-precision integer to a native floating
+ * point number in the range [0.5,1) times a power of two.
+ * NOTE: Intentionally converts to a number that's a few
+ * ulp too small, so that RefineApproximation will not overflow
+ * near the high end of the machine's arithmetic range.
+ *
+ * Results:
+ * Returns the converted number.
+ *
+ * Side effects:
+ * Stores the exponent of two in 'machexp'.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static double
+BignumToBiasedFrExp( mp_int* a,
+ /* Integer to convert */
+ int* machexp )
+ /* Power of two */
+{
+ mp_int b;
+ int bits;
+ int shift;
+ int i;
+ double r;
+
+ /* Determine how many bits we need, and extract that many from
+ * the input. Round to nearest unit in the last place. */
+
+ bits = mp_count_bits( a );
+ shift = mantBits - 2 - bits;
+ mp_init( &b );
+ if ( shift > 0 ) {
+ mp_mul_2d( a, shift, &b );
+ } else if ( shift < 0 ) {
+ mp_div_2d( a, -shift, &b, NULL );
+ } else {
+ mp_copy( a, &b );
+ }
+
+ /* Accumulate the result, one mp_digit at a time */
+
+ r = 0.0;
+ for ( i = b.used-1; i >= 0; --i ) {
+ r = ldexp( r, DIGIT_BIT ) + b.dp[i];
+ }
+ mp_clear( &b );
+
+ /* Return the result with the appropriate sign. */
+
+ *machexp = bits - mantBits + 2;
+ if ( a->sign == MP_ZPOS ) {
+ return r;
+ } else {
+ return -r;
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Pow10TimesFrExp --
+ *
+ * Multiply a power of ten by a number expressed as fraction and
+ * exponent.
+ *
+ * Results:
+ * Returns the significand of the result.
+ *
+ * Side effects:
+ * Overwrites the 'machexp' parameter with the exponent of the
+ * result.
+ *
+ * Assumes that 'exponent' is such that 10**exponent would be a double,
+ * even though 'fraction*10**(machexp+exponent)' might overflow.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static double
+Pow10TimesFrExp( int exponent, /* Power of 10 to multiply by */
+ double fraction,
+ /* Significand of multiplicand */
+ int* machexp ) /* On input, exponent of multiplicand.
+ * On output, exponent of result. */
+{
+ int i, j;
+ int expt = *machexp;
+ double retval = fraction;
+
+ if ( exponent > 0 ) {
+
+ /* Multiply by 10**exponent */
+
+ retval = frexp( retval * pow10[ exponent & 0xf ], &j );
+ expt += j;
+ for ( i = 4; i < 9; ++i ) {
+ if ( exponent & (1<<i) ) {
+ retval = frexp( retval * pow_10_2_n[ i ], &j );
+ expt += j;
+ }
+ }
+ } else if ( exponent < 0 ) {
+
+ /* Divide by 10**-exponent */
+
+ retval = frexp( retval / pow10[ (-exponent) & 0xf ], &j );
+ expt += j;
+ for ( i = 4; i < 9; ++i ) {
+ if ( (-exponent) & (1<<i) ) {
+ retval = frexp( retval / pow_10_2_n[ i ], &j );
+ expt += j;
+ }
+ }
+ }
+
+ *machexp = expt;
+ return retval;
+
+}
/*
*----------------------------------------------------------------------