diff options
Diffstat (limited to 'generic/tclStrToD.c')
-rwxr-xr-x | generic/tclStrToD.c | 2919 |
1 files changed, 350 insertions, 2569 deletions
diff --git a/generic/tclStrToD.c b/generic/tclStrToD.c index 76adf75..fbe4105 100755 --- a/generic/tclStrToD.c +++ b/generic/tclStrToD.c @@ -13,12 +13,20 @@ * * 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.38 2009/07/16 21:24:40 dgp Exp $ + * *---------------------------------------------------------------------- */ -#include "tclInt.h" -#include "tommath.h" +#include <tclInt.h> +#include <stdio.h> +#include <stdlib.h> +#include <float.h> +#include <limits.h> #include <math.h> +#include <ctype.h> +#include <tommath.h> /* * Define KILL_OCTAL to suppress interpretation of numbers with leading zero @@ -63,10 +71,9 @@ typedef unsigned int fpu_control_t __attribute__ ((__mode__ (__HI__))); /* * MIPS floating-point units need special settings in control registers - * to use gradual underflow as we expect. This fix is for the MIPSpro - * compiler. + * to use gradual underflow as we expect. */ -#if defined(__sgi) && defined(_COMPILER_VERSION) +#if defined(__mips) #include <sys/fpu.h> #endif /* @@ -87,66 +94,6 @@ typedef unsigned int fpu_control_t __attribute__ ((__mode__ (__HI__))); * runtime). */ -/* Magic constants */ - -#define LOG10_2 0.3010299956639812 -#define TWO_OVER_3LOG10 0.28952965460216784 -#define LOG10_3HALVES_PLUS_FUDGE 0.1760912590558 - -/* Definitions of the parts of an IEEE754-format floating point number */ - -#define SIGN_BIT 0x80000000 - /* Mask for the sign bit in the first - * word of a double */ -#define EXP_MASK 0x7ff00000 - /* Mask for the exponent field in the - * first word of a double */ -#define EXP_SHIFT 20 - /* Shift count to make the exponent an - * integer */ -#define HIDDEN_BIT (((Tcl_WideUInt) 0x00100000) << 32) - /* Hidden 1 bit for the significand */ -#define HI_ORDER_SIG_MASK 0x000fffff - /* Mask for the high-order part of the - * significand in the first word of a - * double */ -#define SIG_MASK (((Tcl_WideUInt) HI_ORDER_SIG_MASK << 32) \ - | 0xffffffff) - /* Mask for the 52-bit significand. */ -#define FP_PRECISION 53 - /* Number of bits of significand plus the - * hidden bit */ -#define EXPONENT_BIAS 0x3ff - /* Bias of the exponent 0 */ - -/* Derived quantities */ - -#define TEN_PMAX 22 - /* floor(FP_PRECISION*log(2)/log(5)) */ -#define QUICK_MAX 14 - /* floor((FP_PRECISION-1)*log(2)/log(10)) - 1 */ -#define BLETCH 0x10 - /* Highest power of two that is greater than - * DBL_MAX_10_EXP, divided by 16 */ -#define DIGIT_GROUP 8 - /* floor(DIGIT_BIT*log(2)/log(10)) */ - -/* Union used to dismantle floating point numbers. */ - -typedef union Double { - struct { -#ifdef WORDS_BIGENDIAN - int word0; - int word1; -#else - int word1; - int word0; -#endif - } w; - double d; - Tcl_WideUInt q; -} Double; - static int maxpow10_wide; /* The powers of ten that can be represented * exactly as wide integers. */ static Tcl_WideUInt *pow10_wide; @@ -162,11 +109,13 @@ 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 = 0.0; /* The smallest representable double */ +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. */ static const double pow_10_2_n[] = { /* Inexact higher powers of ten. */ 1.0, 100.0, @@ -178,7 +127,6 @@ static const double pow_10_2_n[] = { /* Inexact higher powers of ten. */ 1.0e+128, 1.0e+256 }; - static int n770_fp; /* Flag is 1 on Nokia N770 floating point. * Nokia's floating point has the words * reversed: if big-endian is 7654 3210, @@ -187,151 +135,27 @@ static int n770_fp; /* Flag is 1 on Nokia N770 floating point. * little-endian within the 32-bit words * but big-endian between them. */ -/* Table of powers of 5 that are small enough to fit in an mp_digit. */ - -static const mp_digit dpow5[13] = { - 1, 5, 25, 125, - 625, 3125, 15625, 78125, - 390625, 1953125, 9765625, 48828125, - 244140625 -}; - -/* Table of powers: pow5_13[n] = 5**(13*2**(n+1)) */ -static mp_int pow5_13[5]; /* Table of powers: 5**13, 5**26, 5**52, - * 5**104, 5**208 */ -static const double tens[] = { - 1e00, 1e01, 1e02, 1e03, 1e04, 1e05, 1e06, 1e07, 1e08, 1e09, - 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, - 1e20, 1e21, 1e22 -}; - -static const int itens [] = { - 1, - 10, - 100, - 1000, - 10000, - 100000, - 1000000, - 10000000, - 100000000 -}; - -static const double bigtens[] = { - 1e016, 1e032, 1e064, 1e128, 1e256 -}; -#define N_BIGTENS 5 - -static const int log2pow5[27] = { - 01, 3, 5, 7, 10, 12, 14, 17, 19, 21, - 24, 26, 28, 31, 33, 35, 38, 40, 42, 45, - 47, 49, 52, 54, 56, 59, 61 -}; -#define N_LOG2POW5 27 - -static const Tcl_WideUInt wuipow5[27] = { - (Tcl_WideUInt) 1, /* 5**0 */ - (Tcl_WideUInt) 5, - (Tcl_WideUInt) 25, - (Tcl_WideUInt) 125, - (Tcl_WideUInt) 625, - (Tcl_WideUInt) 3125, /* 5**5 */ - (Tcl_WideUInt) 3125*5, - (Tcl_WideUInt) 3125*25, - (Tcl_WideUInt) 3125*125, - (Tcl_WideUInt) 3125*625, - (Tcl_WideUInt) 3125*3125, /* 5**10 */ - (Tcl_WideUInt) 3125*3125*5, - (Tcl_WideUInt) 3125*3125*25, - (Tcl_WideUInt) 3125*3125*125, - (Tcl_WideUInt) 3125*3125*625, - (Tcl_WideUInt) 3125*3125*3125, /* 5**15 */ - (Tcl_WideUInt) 3125*3125*3125*5, - (Tcl_WideUInt) 3125*3125*3125*25, - (Tcl_WideUInt) 3125*3125*3125*125, - (Tcl_WideUInt) 3125*3125*3125*625, - (Tcl_WideUInt) 3125*3125*3125*3125, /* 5**20 */ - (Tcl_WideUInt) 3125*3125*3125*3125*5, - (Tcl_WideUInt) 3125*3125*3125*3125*25, - (Tcl_WideUInt) 3125*3125*3125*3125*125, - (Tcl_WideUInt) 3125*3125*3125*3125*625, - (Tcl_WideUInt) 3125*3125*3125*3125*3125, /* 5**25 */ - (Tcl_WideUInt) 3125*3125*3125*3125*3125*5 /* 5**26 */ -}; - /* * Static functions defined in this file. */ +static double AbsoluteValue(double v, int *signum); static int AccumulateDecimalDigit(unsigned, int, Tcl_WideUInt *, mp_int *, int); +static double BignumToBiasedFrExp(mp_int *big, int* machexp); +static int GetIntegerTimesPower(double v, mp_int *r, int *e); static double MakeHighPrecisionDouble(int signum, mp_int *significand, int nSigDigs, int exponent); static double MakeLowPrecisionDouble(int signum, Tcl_WideUInt significand, int nSigDigs, int exponent); static double MakeNaN(int signum, Tcl_WideUInt tag); -static double RefineApproximation(double approx, - mp_int *exactSignificand, int exponent); -static void MulPow5(mp_int*, unsigned, mp_int*); -static int NormalizeRightward(Tcl_WideUInt*); -static int RequiredPrecision(Tcl_WideUInt); -static void DoubleToExpAndSig(double, Tcl_WideUInt*, int*, int*); -static void TakeAbsoluteValue(Double*, int*); -static char* FormatInfAndNaN(Double*, int*, char**); -static char* FormatZero(int*, char**); -static int ApproximateLog10(Tcl_WideUInt, int, int); -static int BetterLog10(double, int, int*); -static void ComputeScale(int, int, int*, int*, int*, int*); -static void SetPrecisionLimits(int, int, int*, int*, int*, int*); -static char* BumpUp(char*, char*, int*); -static int AdjustRange(double*, int); -static char* ShorteningQuickFormat(double, int, int, double, - char*, int*); -static char* StrictQuickFormat(double, int, int, double, - char*, int*); -static char* QuickConversion(double, int, int, int, int, int, int, - int*, char**); -static void CastOutPowersOf2(int*, int*, int*); -static char* ShorteningInt64Conversion(Double*, int, Tcl_WideUInt, - int, int, int, int, int, int, int, int, int, - int, int, int*, char**); -static char* StrictInt64Conversion(Double*, int, Tcl_WideUInt, - int, int, int, int, int, int, - int, int, int*, char**); -static int ShouldBankerRoundUpPowD(mp_int*, int, int); -static int ShouldBankerRoundUpToNextPowD(mp_int*, mp_int*, - int, int, int, mp_int*); -static char* ShorteningBignumConversionPowD(Double* dPtr, - int convType, Tcl_WideUInt bw, int b2, int b5, - int m2plus, int m2minus, int m5, - int sd, int k, int len, - int ilim, int ilim1, int* decpt, - char** endPtr); -static char* StrictBignumConversionPowD(Double* dPtr, int convType, - Tcl_WideUInt bw, int b2, int b5, - int sd, int k, int len, - int ilim, int ilim1, int* decpt, - char** endPtr); -static int ShouldBankerRoundUp(mp_int*, mp_int*, int); -static int ShouldBankerRoundUpToNext(mp_int*, mp_int*, mp_int*, - int, int, mp_int*); -static char* ShorteningBignumConversion(Double* dPtr, int convType, - Tcl_WideUInt bw, int b2, - int m2plus, int m2minus, - int s2, int s5, int k, int len, - int ilim, int ilim1, int* decpt, - char** endPtr); -static char* StrictBignumConversion(Double* dPtr, int convType, - Tcl_WideUInt bw, int b2, - int s2, int s5, int k, int len, - int ilim, int ilim1, int* decpt, - char** endPtr); -static double BignumToBiasedFrExp(mp_int *big, int *machexp); +static Tcl_WideUInt Nokia770Twiddle(Tcl_WideUInt w); static double Pow10TimesFrExp(int exponent, double fraction, int *machexp); +static double RefineApproximation(double approx, + mp_int *exactSignificand, int exponent); static double SafeLdExp(double fraction, int exponent); -static Tcl_WideUInt Nokia770Twiddle(Tcl_WideUInt w); /* *---------------------------------------------------------------------- @@ -524,7 +348,7 @@ TclParseNumber( * I, N, and whitespace. */ - if (TclIsSpaceProc(c)) { + if (isspace(UCHAR(c))) { if (flags & TCL_PARSE_NO_WHITESPACE) { goto endgame; } @@ -554,6 +378,8 @@ TclParseNumber( break; } else if (flags & TCL_PARSE_HEXADECIMAL_ONLY) { goto zerox; + } else if (flags & TCL_PARSE_BINARY_ONLY) { + goto zerob; } else if (flags & TCL_PARSE_OCTAL_ONLY) { goto zeroo; } else if (isdigit(UCHAR(c))) { @@ -602,6 +428,9 @@ TclParseNumber( state = ZERO_B; break; } + if (flags & TCL_PARSE_BINARY_ONLY) { + goto zerob; + } if (c == 'o' || c == 'O') { explicitOctal = 1; state = ZERO_O; @@ -626,7 +455,7 @@ TclParseNumber( case ZERO_O: zeroo: if (c == '0') { - numTrailZeros++; + ++numTrailZeros; state = OCTAL; break; } else if (c >= '1' && c <= '7') { @@ -699,7 +528,7 @@ TclParseNumber( */ if (c == '0') { - numTrailZeros++; + ++numTrailZeros; state = BAD_OCTAL; break; } else if (isdigit(UCHAR(c))) { @@ -742,7 +571,7 @@ TclParseNumber( case ZERO_X: zerox: if (c == '0') { - numTrailZeros++; + ++numTrailZeros; state = HEXADECIMAL; break; } else if (isdigit(UCHAR(c))) { @@ -787,8 +616,9 @@ TclParseNumber( acceptPoint = p; acceptLen = len; case ZERO_B: + zerob: if (c == '0') { - numTrailZeros++; + ++numTrailZeros; state = BINARY; break; } else if (c != '1') { @@ -835,7 +665,7 @@ TclParseNumber( acceptPoint = p; acceptLen = len; if (c == '0') { - numTrailZeros++; + ++numTrailZeros; state = DECIMAL; break; } else if (isdigit(UCHAR(c))) { @@ -878,12 +708,12 @@ TclParseNumber( case LEADING_RADIX_POINT: if (c == '0') { - numDigitsAfterDp++; - numTrailZeros++; + ++numDigitsAfterDp; + ++numTrailZeros; state = FRACTION; break; } else if (isdigit(UCHAR(c))) { - numDigitsAfterDp++; + ++numDigitsAfterDp; if (objPtr != NULL) { significandOverflow = AccumulateDecimalDigit( (unsigned)(c-'0'), numTrailZeros, @@ -1038,7 +868,7 @@ TclParseNumber( } /* FALLTHROUGH */ case sNANPAREN: - if (TclIsSpaceProc(c)) { + if (isspace(UCHAR(c))) { break; } if (numSigDigs < 13) { @@ -1048,10 +878,7 @@ TclParseNumber( d = 10 + c - 'a'; } else if (c >= 'A' && c <= 'F') { d = 10 + c - 'A'; - } else { - goto endgame; } - numSigDigs++; significandWide = (significandWide << 4) + d; state = sNANHEX; break; @@ -1066,8 +893,8 @@ TclParseNumber( acceptLen = len; goto endgame; } - p++; - len--; + ++p; + --len; } endgame: @@ -1092,9 +919,9 @@ TclParseNumber( * Accept trailing whitespace. */ - while (len != 0 && TclIsSpaceProc(*p)) { - p++; - len--; + while (len != 0 && isspace(UCHAR(*p))) { + ++p; + --len; } } if (endPtrPtr == NULL) { @@ -1127,14 +954,13 @@ TclParseNumber( case sINFIN: case sINFINI: case sINFINIT: -#ifdef IEEE_FLOATING_POINT case sN: case sNA: case sNANPAREN: case sNANHEX: Tcl_Panic("TclParseNumber: bad acceptState %d parsing '%s'", acceptState, bytes); -#endif + case BINARY: shift = numTrailZeros; if (!significandOverflow && significandWide != 0 && @@ -1315,13 +1141,12 @@ TclParseNumber( objPtr->typePtr = &tclDoubleType; break; -#ifdef IEEE_FLOATING_POINT case sNAN: case sNANFINISH: objPtr->internalRep.doubleValue = MakeNaN(signum, significandWide); objPtr->typePtr = &tclDoubleType; break; -#endif + case INITIAL: /* This case only to silence compiler warning */ Tcl_Panic("TclParseNumber: state INITIAL can't happen here"); @@ -1664,9 +1489,6 @@ MakeHighPrecisionDouble( goto returnValue; } retval = SafeLdExp(retval, machexp); - if (tiny == 0.0) { - tiny = SafeLdExp(1.0, DBL_MIN_EXP * log2FLT_RADIX - mantBits); - } if (retval < tiny) { retval = tiny; } @@ -1880,8 +1702,8 @@ RefineApproximation( */ if (mp_cmp_mag(&twoMd, &twoMv) == MP_LT) { - mp_clear(&twoMd); - mp_clear(&twoMv); + mp_clear(&twoMd); + mp_clear(&twoMv); return approxResult; } @@ -1909,2386 +1731,414 @@ RefineApproximation( } /* - *----------------------------------------------------------------------------- - * - * MultPow5 -- - * - * Multiply a bignum by a power of 5. - * - * Side effects: - * Stores base*5**n in result - * - *----------------------------------------------------------------------------- - */ - -inline static void -MulPow5(mp_int* base, /* Number to multiply */ - unsigned n, /* Power of 5 to multiply by */ - mp_int* result) /* Place to store the result */ -{ - mp_int* p = base; - int n13 = n / 13; - int r = n % 13; - if (r != 0) { - mp_mul_d(p, dpow5[r], result); - p = result; - } - r = 0; - while (n13 != 0) { - if (n13 & 1) { - mp_mul(p, pow5_13+r, result); - p = result; - } - n13 >>= 1; - ++r; - } - if (p != result) { - mp_copy(p, result); - } -} - -/* - *----------------------------------------------------------------------------- - * - * NormalizeRightward -- - * - * Shifts a number rightward until it is odd (that is, until the - * least significant bit is nonzero. - * - * Results: - * Returns the number of bit positions by which the number was shifted. - * - * Side effects: - * Shifts the number in place; *wPtr is replaced by the shifted number. - * - *----------------------------------------------------------------------------- - */ - -inline static int -NormalizeRightward(Tcl_WideUInt* wPtr) - /* INOUT: Number to shift */ -{ - int rv = 0; - Tcl_WideUInt w = *wPtr; - if (!(w & (Tcl_WideUInt) 0xffffffff)) { - w >>= 32; rv += 32; - } - if (!(w & (Tcl_WideUInt) 0xffff)) { - w >>= 16; rv += 16; - } - if (!(w & (Tcl_WideUInt) 0xff)) { - w >>= 8; rv += 8; - } - if (!(w & (Tcl_WideUInt) 0xf)) { - w >>= 4; rv += 4; - } - if (!(w & 0x3)) { - w >>= 2; rv += 2; - } - if (!(w & 0x1)) { - w >>= 1; ++rv; - } - *wPtr = w; - return rv; -} - -/* - *-----------------------------------------------------------------------------0 - * - * RequiredPrecision -- - * - * Determines the number of bits needed to hold an intger. - * - * Results: - * Returns the position of the most significant bit (0 - 63). - * Returns 0 if the number is zero. - * - *---------------------------------------------------------------------------- - */ - -static int -RequiredPrecision(Tcl_WideUInt w) - /* Number to interrogate */ -{ - int rv; - unsigned long wi; - if (w & ((Tcl_WideUInt) 0xffffffff << 32)) { - wi = (unsigned long) (w >> 32); rv = 32; - } else { - wi = (unsigned long) w; rv = 0; - } - if (wi & 0xffff0000) { - wi >>= 16; rv += 16; - } - if (wi & 0xff00) { - wi >>= 8; rv += 8; - } - if (wi & 0xf0) { - wi >>= 4; rv += 4; - } - if (wi & 0xc) { - wi >>= 2; rv += 2; - } - if (wi & 0x2) { - wi >>= 1; ++rv; - } - if (wi & 0x1) { - ++rv; - } - return rv; -} - -/* - *----------------------------------------------------------------------------- - * - * DoubleToExpAndSig -- - * - * Separates a 'double' into exponent and significand. - * - * Side effects: - * Stores the significand in '*significand' and the exponent in - * '*expon' so that dv == significand * 2.0**expon, and significand - * is odd. Also stores the position of the leftmost 1-bit in 'significand' - * in 'bits'. - * - *----------------------------------------------------------------------------- - */ - -inline static void -DoubleToExpAndSig(double dv, /* Number to convert */ - Tcl_WideUInt* significand, - /* OUTPUT: Significand of the number */ - int* expon, /* OUTPUT: Exponent to multiply the number by */ - int* bits) /* OUTPUT: Number of significant bits */ -{ - Double d; /* Number being converted */ - Tcl_WideUInt z; /* Significand under construction */ - int de; /* Exponent of the number */ - int k; /* Bit count */ - - d.d = dv; - - /* Extract exponent and significand */ - - de = (d.w.word0 & EXP_MASK) >> EXP_SHIFT; - z = d.q & SIG_MASK; - if (de != 0) { - z |= HIDDEN_BIT; - k = NormalizeRightward(&z); - *bits = FP_PRECISION - k; - *expon = k + (de - EXPONENT_BIAS) - (FP_PRECISION-1); - } else { - k = NormalizeRightward(&z); - *expon = k + (de - EXPONENT_BIAS) - (FP_PRECISION-1) + 1; - *bits = RequiredPrecision(z); - } - *significand = z; -} - -/* - *----------------------------------------------------------------------------- - * - * TakeAbsoluteValue -- - * - * Takes the absolute value of a 'double' including 0, Inf and NaN - * - * Side effects: - * The 'double' in *d is replaced with its absolute value. The - * signum is stored in 'sign': 1 for negative, 0 for nonnegative. - * - *----------------------------------------------------------------------------- - */ - -inline static void -TakeAbsoluteValue(Double* d, /* Number to replace with absolute value */ - int* sign) /* Place to put the signum */ -{ - if (d->w.word0 & SIGN_BIT) { - *sign = 1; - d->w.word0 &= ~SIGN_BIT; - } else { - *sign = 0; - } -} - -/* - *----------------------------------------------------------------------------- - * - * FormatInfAndNaN -- - * - * Bailout for formatting infinities and Not-A-Number. - * - * Results: - * Returns one of the strings 'Infinity' and 'NaN'. - * - * Side effects: - * Stores 9999 in *decpt, and sets '*endPtr' to designate the - * terminating NUL byte of the string if 'endPtr' is not NULL. - * - * The string returned must be freed by the caller using 'ckfree'. - * - *----------------------------------------------------------------------------- - */ - -inline static char* -FormatInfAndNaN(Double* d, /* Exceptional number to format */ - int* decpt, /* Decimal point to set to a bogus value */ - char** endPtr) /* Pointer to the end of the formatted data */ -{ - char* retval; - *decpt = 9999; - if (!(d->w.word1) && !(d->w.word0 & HI_ORDER_SIG_MASK)) { - retval = ckalloc(9); - strcpy(retval, "Infinity"); - if (endPtr) { - *endPtr = retval + 8; - } - } else { - retval = ckalloc(4); - strcpy(retval, "NaN"); - if (endPtr) { - *endPtr = retval + 3; - } - } - return retval; -} - -/* - *----------------------------------------------------------------------------- + *---------------------------------------------------------------------- * - * FormatZero -- + * TclDoubleDigits -- * - * Bailout to format a zero floating-point number. + * Converts a double to a string of digits. * * Results: - * Returns the constant string "0" + * Returns the position of the character in the string after which the + * decimal point should appear. Since the string contains only + * significant digits, the position may be less than zero or greater than + * the length of the string. * * Side effects: - * Stores 1 in '*decpt' and puts a pointer to the NUL byte terminating - * the string in '*endPtr' if 'endPtr' is not NULL. + * Stores the digits in the given buffer and sets 'signum' according to + * the sign of the number. * - *----------------------------------------------------------------------------- - */ - -inline static char* -FormatZero(int* decpt, /* Location of the decimal point */ - char** endPtr) /* Pointer to the end of the formatted data */ -{ - char* retval = ckalloc(2); - strcpy(retval, "0"); - if (endPtr) { - *endPtr = retval+1; - } - *decpt = 0; - return retval; -} + *---------------------------------------------------------------------- -/* - *----------------------------------------------------------------------------- - * - * ApproximateLog10 -- - * - * Computes a two-term Taylor series approximation to the common - * log of a number, and computes the number's binary log. - * - * Results: - * Return an approximation to floor(log10(bw*2**be)) that is either - * exact or 1 too high. - * - *----------------------------------------------------------------------------- */ -inline static int -ApproximateLog10(Tcl_WideUInt bw, - /* Integer significand of the number */ - int be, /* Power of two to scale bw */ - int bbits) /* Number of bits of precision in bw */ +int +TclDoubleDigits( + char *buffer, /* 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. */ { - int i; /* Log base 2 of the number */ - int k; /* Floor(Log base 10 of the number) */ - double ds; /* Mantissa of the number */ - Double d2; + 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. */ + mp_int mminus; /* Scaled epsilon: (r - 2*mminus) == v(-) + * where v(-) is the floating point + * predecessor of v. */ + mp_int temp; + int rfac2 = 0; /* Powers of 2 and 5 by which large */ + int rfac5 = 0; /* integers should be scaled. */ + int sfac2 = 0; + int sfac5 = 0; + int mplusfac2 = 0; + int mminusfac2 = 0; + char c; + int i, k, n; /* - * Compute i and d2 such that d = d2*2**i, and 1 < d2 < 2. - * Compute an approximation to log10(d), - * log10(d) ~ log10(2) * i + log10(1.5) - * + (significand-1.5)/(1.5 * log(10)) + * Split the number into absolute value and signum. */ - d2.q = bw << (FP_PRECISION - bbits) & SIG_MASK; - d2.w.word0 |= (EXPONENT_BIAS) << EXP_SHIFT; - i = be + bbits - 1; - ds = (d2.d - 1.5) * TWO_OVER_3LOG10 - + LOG10_3HALVES_PLUS_FUDGE - + LOG10_2 * i; - k = (int) ds; - if (k > ds) { - --k; - } - return k; -} + v = AbsoluteValue(v, signum); -/* - *----------------------------------------------------------------------------- - * - * BetterLog10 -- - * - * Improves the result of ApproximateLog10 for numbers in the range - * 1 .. 10**(TEN_PMAX)-1 - * - * Side effects: - * Sets k_check to 0 if the new result is known to be exact, and to - * 1 if it may still be one too high. - * - * Results: - * Returns the improved approximation to log10(d) - * - *----------------------------------------------------------------------------- - */ - -inline static int -BetterLog10(double d, /* Original number to format */ - int k, /* Characteristic(Log base 10) of the number */ - int* k_check) /* Flag == 1 if k is inexact */ -{ - /* - * Performance hack. If k is in the range 0..TEN_PMAX, then we can - * use a powers-of-ten table to check it. - */ - if (k >= 0 && k <= TEN_PMAX) { - if (d < tens[k]) { - k--; - } - *k_check = 0; - } else { - *k_check = 1; - } - return k; -} - -/* - *----------------------------------------------------------------------------- - * - * ComputeScale -- - * - * Prepares to format a floating-point number as decimal. - * - * Parameters: - * floor(log10*x) is k (or possibly k-1). floor(log2(x) is i. - * The significand of x requires bbits bits to represent. - * - * Results: - * Determines integers b2, b5, s2, s5 so that sig*2**b2*5**b5/2**s2*2**s5 - * exactly represents the value of the x/10**k. This value will lie - * in the range [1 .. 10), and allows for computing successive digits - * by multiplying sig%10 by 10. - * - *----------------------------------------------------------------------------- - */ - -inline static void -ComputeScale(int be, /* Exponent part of number: d = bw * 2**be */ - int k, /* Characteristic of log10(number) */ - int* b2, /* OUTPUT: Power of 2 in the numerator */ - int* b5, /* OUTPUT: Power of 5 in the numerator */ - int* s2, /* OUTPUT: Power of 2 in the denominator */ - int* s5) /* OUTPUT: Power of 5 in the denominator */ -{ - - /* - * Scale numerator and denominator powers of 2 so that the - * input binary number is the ratio of integers + /* + * Handle zero specially. */ - if (be <= 0) { - *b2 = 0; - *s2 = -be; - } else { - *b2 = be; - *s2 = 0; - } - /* - * Scale numerator and denominator so that the output decimal number - * is the ratio of integers - */ - if (k >= 0) { - *b5 = 0; - *s5 = k; - *s2 += k; - } else { - *b2 -= k; - *b5 = -k; - *s5 = 0; - } -} - -/* - *----------------------------------------------------------------------------- - * - * SetPrecisionLimits -- - * - * Determines how many digits of significance should be computed - * (and, hence, how much memory need be allocated) for formatting a - * floating point number. - * - * Given that 'k' is floor(log10(x)): - * if 'shortest' format is used, there will be at most 18 digits in the result. - * if 'F' format is used, there will be at most 'ndigits' + k + 1 digits - * if 'E' format is used, there will be exactly 'ndigits' digits. - * - * Side effects: - * Adjusts '*ndigitsPtr' to have a valid value. - * Stores the maximum memory allocation needed in *iPtr. - * Sets '*iLimPtr' to the limiting number of digits to convert if k - * has been guessed correctly, and '*iLim1Ptr' to the limiting number - * of digits to convert if k has been guessed to be one too high. - * - *----------------------------------------------------------------------------- - */ - -inline static void -SetPrecisionLimits(int convType, - /* Type of conversion: - * TCL_DD_SHORTEST - * TCL_DD_STEELE0 - * TCL_DD_E_FMT - * TCL_DD_F_FMT */ - int k, /* Floor(log10(number to convert)) */ - int* ndigitsPtr, - /* IN/OUT: Number of digits requested - * (Will be adjusted if needed) */ - int* iPtr, /* OUT: Maximum number of digits - * to return */ - int *iLimPtr,/* OUT: Number of digits of significance - * if the bignum method is used.*/ - int *iLim1Ptr) - /* OUT: Number of digits of significance - * if the quick method is used. */ -{ - switch(convType) { - case TCL_DD_SHORTEST0: - case TCL_DD_STEELE0: - *iLimPtr = *iLim1Ptr = -1; - *iPtr = 18; - *ndigitsPtr = 0; - break; - case TCL_DD_E_FORMAT: - if (*ndigitsPtr <= 0) { - *ndigitsPtr = 1; - } - *iLimPtr = *iLim1Ptr = *iPtr = *ndigitsPtr; - break; - case TCL_DD_F_FORMAT: - *iPtr = *ndigitsPtr + k + 1; - *iLimPtr = *iPtr; - *iLim1Ptr = *iPtr - 1; - if (*iPtr <= 0) { - *iPtr = 1; - } - break; - default: - *iPtr = -1; - *iLimPtr = -1; - *iLim1Ptr = -1; - Tcl_Panic("impossible conversion type in TclDoubleDigits"); - } -} - -/* - *----------------------------------------------------------------------------- - * - * BumpUp -- - * - * Increases a string of digits ending in a series of nines to - * designate the next higher number. xxxxb9999... -> xxxx(b+1)0000... - * - * Results: - * Returns a pointer to the end of the adjusted string. - * - * Side effects: - * In the case that the string consists solely of '999999', sets it - * to "1" and moves the decimal point (*kPtr) one place to the right. - * - *----------------------------------------------------------------------------- - */ - - -inline static char* -BumpUp(char* s, /* Cursor pointing one past the end of the - * string */ - char* retval, /* Start of the string of digits */ - int* kPtr) /* Position of the decimal point */ -{ - while (*--s == '9') { - if (s == retval) { - ++(*kPtr); - *s = '1'; - return s+1; - } - } - ++*s; - ++s; - return s; -} - -/* - *----------------------------------------------------------------------------- - * - * AdjustRange -- - * - * Rescales a 'double' in preparation for formatting it using the - * 'quick' double-to-string method. - * - * Results: - * Returns the precision that has been lost in the prescaling as - * a count of units in the least significant place. - * - *----------------------------------------------------------------------------- - */ - -inline static int -AdjustRange(double* dPtr, /* INOUT: Number to adjust */ - int k) /* IN: floor(log10(d)) */ -{ - int ieps; /* Number of roundoff errors that have - * accumulated */ - double d = *dPtr; /* Number to adjust */ - double ds; - int i, j, j1; - - ieps = 2; - - if (k > 0) { - /* - * The number must be reduced to bring it into range. - */ - ds = tens[k & 0xf]; - j = k >> 4; - if (j & BLETCH) { - j &= (BLETCH-1); - d /= bigtens[N_BIGTENS - 1]; - ieps++; - } - i = 0; - for (; j != 0; j>>=1) { - if (j & 1) { - ds *= bigtens[i]; - ++ieps; - } - ++i; - } - d /= ds; - } else if ((j1 = -k) != 0) { - /* - * The number must be increased to bring it into range - */ - d *= tens[j1 & 0xf]; - i = 0; - for (j = j1>>4; j; j>>=1) { - if (j & 1) { - ieps++; - d *= bigtens[i]; - } - ++i; - } - } - - *dPtr = d; - return ieps; -} - -/* - *----------------------------------------------------------------------------- - * - * ShorteningQuickFormat -- - * - * Returns a 'quick' format of a double precision number to a string - * of digits, preferring a shorter string of digits if the shorter - * string is still within 1/2 ulp of the number. - * - * Results: - * Returns the string of digits. Returns NULL if the 'quick' method - * fails and the bignum method must be used. - * - * Side effects: - * Stores the position of the decimal point at '*kPtr'. - * - *----------------------------------------------------------------------------- - */ - -inline static char* -ShorteningQuickFormat(double d, /* Number to convert */ - int k, /* floor(log10(d)) */ - int ilim, /* Number of significant digits to return */ - double eps, - /* Estimated roundoff error */ - char* retval, - /* Buffer to receive the digit string */ - int* kPtr) - /* Pointer to stash the position of - * the decimal point */ -{ - char* s = retval; /* Cursor in the return value */ - int digit; /* Current digit */ - int i; - - eps = 0.5 / tens[ilim-1] - eps; - i = 0; - for (;;) { - /* Convert a digit */ - - digit = (int) d; - d -= digit; - *s++ = '0' + digit; - - /* - * Truncate the conversion if the string of digits is within - * 1/2 ulp of the actual value. - */ - - if (d < eps) { - *kPtr = k; - return s; - } - if ((1. - d) < eps) { - *kPtr = k; - return BumpUp(s, retval, kPtr); - } - - /* - * Bail out if the conversion fails to converge to a sufficiently - * precise value - */ - - if (++i >= ilim) { - return NULL; - } - - /* - * Bring the next digit to the integer part. - */ - - eps *= 10; - d *= 10.0; - } -} - -/* - *----------------------------------------------------------------------------- - * - * StrictQuickFormat -- - * - * Convert a double precision number of a string of a precise number - * of digits, using the 'quick' double precision method. - * - * Results: - * Returns the digit string, or NULL if the bignum method must be - * used to do the formatting. - * - * Side effects: - * Stores the position of the decimal point in '*kPtr'. - * - *----------------------------------------------------------------------------- - */ - -inline static char* -StrictQuickFormat(double d, /* Number to convert */ - int k, /* floor(log10(d)) */ - int ilim, /* Number of significant digits to return */ - double eps, /* Estimated roundoff error */ - char* retval, /* Start of the digit string */ - int* kPtr) /* Pointer to stash the position of - * the decimal point */ -{ - char* s = retval; /* Cursor in the return value */ - int digit; /* Current digit of the answer */ - int i; - - eps *= tens[ilim-1]; - i = 1; - for (;;) { - /* Extract a digit */ - digit = (int) d; - d -= digit; - if (d == 0.0) { - ilim = i; - } - *s++ = '0' + digit; - - /* - * When the given digit count is reached, handle trailing strings - * of 0 and 9. - */ - if (i == ilim) { - if (d > 0.5 + eps) { - *kPtr = k; - return BumpUp(s, retval, kPtr); - } else if (d < 0.5 - eps) { - while (*--s == '0') { - /* do nothing */ - } - s++; - *kPtr = k; - return s; - } else { - return NULL; - } - } - - /* Advance to the next digit */ - ++i; - d *= 10.0; + if (v == 0.0) { + *buffer++ = '0'; + *buffer++ = '\0'; + return 1; } -} - -/* - *----------------------------------------------------------------------------- - * - * QuickConversion -- - * - * Converts a floating point number the 'quick' way, when only a limited - * number of digits is required and floating point arithmetic can - * therefore be used for the intermediate results. - * - * Results: - * Returns the converted string, or NULL if the bignum method must - * be used. - * - *----------------------------------------------------------------------------- - */ - -inline static char* -QuickConversion(double e, /* Number to format */ - int k, /* floor(log10(d)), approximately */ - int k_check, /* 0 if k is exact, 1 if it may be too high */ - int flags, /* Flags passed to dtoa: - * TCL_DD_SHORTEN_FLAG */ - int len, /* Length of the return value */ - int ilim, /* Number of digits to store */ - int ilim1, /* Number of digits to store if we - * musguessed k */ - int* decpt, /* OUTPUT: Location of the decimal point */ - char** endPtr) /* OUTPUT: Pointer to the terminal null byte */ -{ - int ieps; /* Number of 1-ulp roundoff errors that have - * accumulated in the calculation*/ - Double eps; /* Estimated roundoff error */ - char* retval; /* Returned string */ - char* end; /* Pointer to the terminal null byte in the - * returned string */ - volatile double d; /* Workaround for a bug in mingw gcc 3.4.5 */ /* - * Bring d into the range [1 .. 10) + * 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. */ - ieps = AdjustRange(&e, k); - d = e; - /* - * If the guessed value of k didn't get d into range, adjust it - * by one. If that leaves us outside the range in which quick format - * is accurate, bail out. - */ - if (k_check && d < 1. && ilim > 0) { - if (ilim1 < 0) { - return NULL; - } - ilim = ilim1; - --k; - d *= 10.0; - ++ieps; - } + smallestSig = GetIntegerTimesPower(v, &r, &e); - /* - * Compute estimated roundoff error - */ - eps.d = ieps * d + 7.; - eps.w.word0 -= (FP_PRECISION-1) << EXP_SHIFT; + lowOK = highOK = (mp_iseven(&r)); /* - * Handle the peculiar case where the result has no significant - * digits. + * We are going to want to develop integers r, s, mplus, and mminus such + * that v = r / s, v(+)-v / 2 = mplus / s; v-v(-) / 2 = mminus / s and + * then scale either s or r, mplus, mminus by an appropriate power of ten. + * + * We actually do this by keeping track of the powers of 2 and 5 by which + * f is multiplied to yield v and by which 1 is multiplied to yield s, + * mplus, and mminus. */ - retval = ckalloc(len + 1); - if (ilim == 0) { - d -= 5.; - if (d > eps.d) { - *retval = '1'; - *decpt = k; - return retval; - } else if (d < -eps.d) { - *decpt = k; - return retval; - } else { - ckfree(retval); - return NULL; - } - } - /* Format the digit string */ + if (e >= 0) { + int bits = e * log2FLT_RADIX; - if (flags & TCL_DD_SHORTEN_FLAG) { - end = ShorteningQuickFormat(d, k, ilim, eps.d, retval, decpt); - } else { - end = StrictQuickFormat(d, k, ilim, eps.d, retval, decpt); - } - if (end == NULL) { - ckfree(retval); - return NULL; - } - *end = '\0'; - if (endPtr != NULL) { - *endPtr = end; - } - return retval; -} - -/* - *----------------------------------------------------------------------------- - * - * CastOutPowersOf2 -- - * - * Adjust the factors 'b2', 'm2', and 's2' to cast out common powers - * of 2 from numerator and denominator in preparation for the 'bignum' - * method of floating point conversion. - * - *----------------------------------------------------------------------------- - */ + if (!smallestSig) { + /* + * Normal case, m+ and m- are both FLT_RADIX**e + */ -inline static void -CastOutPowersOf2(int* b2, /* Power of 2 to multiply the significand */ - int* m2, /* Power of 2 to multiply 1/2 ulp */ - int* s2) /* Power of 2 to multiply the common - * denominator */ -{ - int i; - if (*m2 > 0 && *s2 > 0) { /* Find the smallest power of 2 in the - * numerator */ - if (*m2 < *s2) { /* Find the lowest common denominatorr */ - i = *m2; + rfac2 = bits + 1; + sfac2 = 1; + mplusfac2 = bits; + mminusfac2 = bits; } else { - i = *s2; - } - *b2 -= i; /* Reduce to lowest terms */ - *m2 -= i; - *s2 -= i; - } -} - -/* - *----------------------------------------------------------------------------- - * - * ShorteningInt64Conversion -- - * - * Converts a double-precision number to the shortest string of - * digits that reconverts exactly to the given number, or to - * 'ilim' digits if that will yield a shorter result. The numerator and - * denominator in David Gay's conversion algorithm are known to fit - * in Tcl_WideUInt, giving considerably faster arithmetic than mp_int's. - * - * Results: - * Returns the string of significant decimal digits, in newly - * allocated memory - * - * Side effects: - * Stores the location of the decimal point in '*decpt' and the - * location of the terminal null byte in '*endPtr'. - * - *----------------------------------------------------------------------------- - */ - -inline static char* -ShorteningInt64Conversion(Double* dPtr, - /* Original number to convert */ - int convType, - /* Type of conversion (shortest, Steele, - E format, F format) */ - Tcl_WideUInt bw, - /* Integer significand */ - int b2, int b5, - /* Scale factor for the significand - * in the numerator */ - int m2plus, int m2minus, int m5, - /* Scale factors for 1/2 ulp in - * the numerator (will be different if - * bw == 1 */ - int s2, int s5, - /* Scale factors for the denominator */ - int k, - /* Number of output digits before the decimal - * point */ - int len, - /* Number of digits to allocate */ - int ilim, - /* Number of digits to convert if b >= s */ - int ilim1, - /* Number of digits to convert if b < s */ - int* decpt, - /* OUTPUT: Position of the decimal point */ - char** endPtr) - /* OUTPUT: Position of the terminal '\0' - * at the end of the returned string */ -{ - - char* retval = ckalloc(len + 1); - /* Output buffer */ - Tcl_WideUInt b = (bw * wuipow5[b5]) << b2; - /* Numerator of the fraction being converted */ - Tcl_WideUInt S = wuipow5[s5] << s2; - /* Denominator of the fraction being - * converted */ - Tcl_WideUInt mplus, mminus; /* Ranges for testing whether the result - * is within roundoff of being exact */ - int digit; /* Current output digit */ - char* s = retval; /* Cursor in the output buffer */ - int i; /* Current position in the output buffer */ - - /* Adjust if the logarithm was guessed wrong */ - - if (b < S) { - b = 10 * b; - ++m2plus; ++m2minus; ++m5; - ilim = ilim1; - --k; - } - - /* Compute roundoff ranges */ - - mplus = wuipow5[m5] << m2plus; - mminus = wuipow5[m5] << m2minus; - - /* Loop through the digits */ - - i = 1; - for (;;) { - digit = (int)(b / S); - if (digit > 10) { - Tcl_Panic("wrong digit!"); - } - b = b % S; - - /* - * Does the current digit put us on the low side of the exact value - * but within within roundoff of being exact? - */ - if (b < mplus - || (b == mplus - && convType != TCL_DD_STEELE0 - && (dPtr->w.word1 & 1) == 0)) { /* - * Make sure we shouldn't be rounding *up* instead, - * in case the next number above is closer + * If f is equal to the smallest significand, then we need another + * factor of FLT_RADIX in s to cope with stepping to the next + * smaller exponent when going to e's predecessor. */ - if (2 * b > S - || (2 * b == S - && (digit & 1) != 0)) { - ++digit; - if (digit == 10) { - *s++ = '9'; - s = BumpUp(s, retval, &k); - break; - } - } - /* Stash the current digit */ - - *s++ = '0' + digit; - break; + rfac2 = bits + log2FLT_RADIX + 1; + sfac2 = 1 + log2FLT_RADIX; + mplusfac2 = bits + log2FLT_RADIX; + mminusfac2 = bits; } - - /* - * Does one plus the current digit put us within roundoff of the - * number? - */ - if (b > S - mminus - || (b == S - mminus - && convType != TCL_DD_STEELE0 - && (dPtr->w.word1 & 1) == 0)) { - if (digit == 9) { - *s++ = '9'; - s = BumpUp(s, retval, &k); - break; - } - ++digit; - *s++ = '0' + digit; - break; - } - + } else { /* - * Have we converted all the requested digits? + * v has digits after the binary point */ - *s++ = '0' + digit; - if (i == ilim) { - if (2*b > S - || (2*b == S && (digit & 1) != 0)) { - s = BumpUp(s, retval, &k); - } - break; - } - - /* Advance to the next digit */ - - b = 10 * b; - mplus = 10 * mplus; - mminus = 10 * mminus; - ++i; - } - - /* - * Endgame - store the location of the decimal point and the end of the - * string. - */ - *s = '\0'; - *decpt = k; - if (endPtr) { - *endPtr = s; - } - return retval; -} - -/* - *----------------------------------------------------------------------------- - * - * StrictInt64Conversion -- - * - * Converts a double-precision number to a fixed-length string of - * 'ilim' digits that reconverts exactly to the given number. - * ('ilim' should be replaced with 'ilim1' in the case where - * log10(d) has been overestimated). The numerator and - * denominator in David Gay's conversion algorithm are known to fit - * in Tcl_WideUInt, giving considerably faster arithmetic than mp_int's. - * - * Results: - * Returns the string of significant decimal digits, in newly - * allocated memory - * - * Side effects: - * Stores the location of the decimal point in '*decpt' and the - * location of the terminal null byte in '*endPtr'. - * - *----------------------------------------------------------------------------- - */ - -inline static char* -StrictInt64Conversion(Double* dPtr, - /* Original number to convert */ - int convType, - /* Type of conversion (shortest, Steele, - E format, F format) */ - Tcl_WideUInt bw, - /* Integer significand */ - int b2, int b5, - /* Scale factor for the significand - * in the numerator */ - int s2, int s5, - /* Scale factors for the denominator */ - int k, - /* Number of output digits before the decimal - * point */ - int len, - /* Number of digits to allocate */ - int ilim, - /* Number of digits to convert if b >= s */ - int ilim1, - /* Number of digits to convert if b < s */ - int* decpt, - /* OUTPUT: Position of the decimal point */ - char** endPtr) - /* OUTPUT: Position of the terminal '\0' - * at the end of the returned string */ -{ - - char* retval = ckalloc(len + 1); - /* Output buffer */ - Tcl_WideUInt b = (bw * wuipow5[b5]) << b2; - /* Numerator of the fraction being converted */ - Tcl_WideUInt S = wuipow5[s5] << s2; - /* Denominator of the fraction being - * converted */ - int digit; /* Current output digit */ - char* s = retval; /* Cursor in the output buffer */ - int i; /* Current position in the output buffer */ - - /* Adjust if the logarithm was guessed wrong */ - - if (b < S) { - b = 10 * b; - ilim = ilim1; - --k; - } - /* Loop through the digits */ + 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. + */ - i = 1; - for (;;) { - digit = (int)(b / S); - if (digit > 10) { - Tcl_Panic("wrong digit!"); - } - b = b % S; + rfac2 = 1; + sfac2 = 1 - e * log2FLT_RADIX; + mplusfac2 = 0; + mminusfac2 = 0; + } else { + /* + * f is the smallest significand, but e is not the smallest + * exponent. We need to scale by FLT_RADIX again to cope with the + * fact that v's predecessor has a smaller exponent. + */ - /* - * Have we converted all the requested digits? - */ - *s++ = '0' + digit; - if (i == ilim) { - if (2*b > S - || (2*b == S && (digit & 1) != 0)) { - s = BumpUp(s, retval, &k); - } else { - while (*--s == '0') { - /* do nothing */ - } - ++s; - } - break; + rfac2 = 1 + log2FLT_RADIX; + sfac2 = 1 + log2FLT_RADIX * (1 - e); + mplusfac2 = FLT_RADIX; + mminusfac2 = 0; } - - /* Advance to the next digit */ - - b = 10 * b; - ++i; } - /* - * Endgame - store the location of the decimal point and the end of the - * string. + /* + * Estimate the highest power of ten that will be needed to hold the + * result. */ - *s = '\0'; - *decpt = k; - if (endPtr) { - *endPtr = s; - } - return retval; -} - -/* - *----------------------------------------------------------------------------- - * - * ShouldBankerRoundUpPowD -- - * - * Test whether bankers' rounding should round a digit up. Assumption - * is made that the denominator of the fraction being tested is - * a power of 2**DIGIT_BIT. - * - * Results: - * Returns 1 iff the fraction is more than 1/2, or if the fraction - * is exactly 1/2 and the digit is odd. - * - *----------------------------------------------------------------------------- - */ -inline static int -ShouldBankerRoundUpPowD(mp_int* b, - /* Numerator of the fraction */ - int sd, /* Denominator is 2**(sd*DIGIT_BIT) */ - int isodd) - /* 1 if the digit is odd, 0 if even */ -{ - int i; - static const mp_digit topbit = (1<<(DIGIT_BIT-1)); - if (b->used < sd || (b->dp[sd-1] & topbit) == 0) { - return 0; - } - if (b->dp[sd-1] != topbit) { - return 1; - } - for (i = sd-2; i >= 0; --i) { - if (b->dp[i] != 0) { - return 1; - } + k = (int) ceil(log(v) / log(10.)); + if (k >= 0) { + sfac2 += k; + sfac5 = k; + } else { + rfac2 -= k; + mplusfac2 -= k; + mminusfac2 -= k; + rfac5 = -k; } - return isodd; -} - -/* - *----------------------------------------------------------------------------- - * - * ShouldBankerRoundUpToNextPowD -- - * - * Tests whether bankers' rounding will round down in the - * "denominator is a power of 2**MP_DIGIT" case. - * - * Results: - * Returns 1 if the rounding will be performed - which increases the - * digit by one - and 0 otherwise. - * - *----------------------------------------------------------------------------- - */ -inline static int -ShouldBankerRoundUpToNextPowD(mp_int* b, - /* Numerator of the fraction */ - mp_int* m, - /* Numerator of the rounding tolerance */ - int sd, - /* Common denominator is 2**(sd*DIGIT_BIT) */ - int convType, - /* Conversion type: STEELE defeats - * round-to-even (Not sure why one wants to - * do this; I copied it from Gay) FIXME */ - int isodd, - /* 1 if the integer significand is odd */ - mp_int* temp) - /* Work area for the calculation */ -{ - int i; - - /* - * Compare B and S-m -- which is the same as comparing B+m and S -- - * which we do by computing b+m and doing a bitwhack compare against - * 2**(DIGIT_BIT*sd) + /* + * Scale r, s, mplus, mminus by the appropriate powers of 2 and 5. */ - mp_add(b, m, temp); - if (temp->used <= sd) { /* too few digits to be > S */ - return 0; - } - if (temp->used > sd+1 || temp->dp[sd] > 1) { - /* >= 2s */ - return 1; - } - for (i = sd-1; i >= 0; --i) { - /* check for ==s */ - if (temp->dp[i] != 0) { /* > s */ - return 1; + + mp_init_set(&mplus, 1); + for (i=0 ; i<=8 ; ++i) { + if (rfac5 & (1 << i)) { + mp_mul(&mplus, pow5+i, &mplus); } } - if (convType == TCL_DD_STEELE0) { - /* biased rounding */ - return 0; + mp_mul(&r, &mplus, &r); + mp_mul_2d(&r, rfac2, &r); + mp_init_copy(&mminus, &mplus); + mp_mul_2d(&mplus, mplusfac2, &mplus); + mp_mul_2d(&mminus, mminusfac2, &mminus); + mp_init_set(&s, 1); + for (i=0 ; i<=8 ; ++i) { + if (sfac5 & (1 << i)) { + mp_mul(&s, pow5+i, &s); + } } - return isodd; -} - -/* - *----------------------------------------------------------------------------- - * - * ShorteningBignumConversionPowD -- - * - * Converts a double-precision number to the shortest string of - * digits that reconverts exactly to the given number, or to - * 'ilim' digits if that will yield a shorter result. The denominator - * in David Gay's conversion algorithm is known to be a power of - * 2**DIGIT_BIT, and hence the division in the main loop may be replaced - * by a digit shift and mask. - * - * Results: - * Returns the string of significant decimal digits, in newly - * allocated memory - * - * Side effects: - * Stores the location of the decimal point in '*decpt' and the - * location of the terminal null byte in '*endPtr'. - * - *----------------------------------------------------------------------------- - */ + mp_mul_2d(&s, sfac2, &s); -inline static char* -ShorteningBignumConversionPowD(Double* dPtr, - /* Original number to convert */ - int convType, - /* Type of conversion (shortest, Steele, - E format, F format) */ - Tcl_WideUInt bw, - /* Integer significand */ - int b2, int b5, - /* Scale factor for the significand - * in the numerator */ - int m2plus, int m2minus, int m5, - /* Scale factors for 1/2 ulp in - * the numerator (will be different if - * bw == 1 */ - int sd, - /* Scale factor for the denominator */ - int k, - /* Number of output digits before the decimal - * point */ - int len, - /* Number of digits to allocate */ - int ilim, - /* Number of digits to convert if b >= s */ - int ilim1, - /* Number of digits to convert if b < s */ - int* decpt, - /* OUTPUT: Position of the decimal point */ - char** endPtr) - /* OUTPUT: Position of the terminal '\0' - * at the end of the returned string */ -{ - - char* retval = ckalloc(len + 1); - /* Output buffer */ - mp_int b; /* Numerator of the fraction being converted */ - mp_int mplus, mminus; /* Bounds for roundoff */ - mp_digit digit; /* Current output digit */ - char* s = retval; /* Cursor in the output buffer */ - int i; /* Index in the output buffer */ - mp_int temp; - int r1; - - /* - * b = bw * 2**b2 * 5**b5 - * mminus = 5**m5 + /* + * It is possible for k to be off by one because we used an inexact + * logarithm. */ - TclBNInitBignumFromWideUInt(&b, bw); - mp_init_set_int(&mminus, 1); - MulPow5(&b, b5, &b); - mp_mul_2d(&b, b2, &b); - - /* Adjust if the logarithm was guessed wrong */ - - if (b.used <= sd) { - mp_mul_d(&b, 10, &b); - ++m2plus; ++m2minus; ++m5; - ilim = ilim1; - --k; + mp_init(&temp); + mp_add(&r, &mplus, &temp); + i = mp_cmp_mag(&temp, &s); + if (i>0 || (highOK && i==0)) { + mp_mul_d(&s, 10, &s); + ++k; + } else { + mp_mul_d(&temp, 10, &temp); + i = mp_cmp_mag(&temp, &s); + if (i<0 || (highOK && i==0)) { + mp_mul_d(&r, 10, &r); + mp_mul_d(&mplus, 10, &mplus); + mp_mul_d(&mminus, 10, &mminus); + --k; + } } /* - * mminus = 5**m5 * 2**m2minus - * mplus = 5**m5 * 2**m2plus + * At this point, k contains the power of ten by which we're scaling the + * result. r/s is at least 1/10 and strictly less than ten, and v = r/s * + * 10**k. mplus and mminus give the rounding limits. */ - mp_mul_2d(&mminus, m2minus, &mminus); - MulPow5(&mminus, m5, &mminus); - if (m2plus > m2minus) { - mp_init_copy(&mplus, &mminus); - mp_mul_2d(&mplus, m2plus-m2minus, &mplus); - } - mp_init(&temp); - - /* Loop through the digits. Do division and mod by s == 2**(sd*DIGIT_BIT) - * by mp_digit extraction */ - - i = 0; for (;;) { - if (b.used <= sd) { - digit = 0; + int tc1, tc2; + + mp_mul_d(&r, 10, &r); + mp_div(&r, &s, &temp, &r); /* temp = 10r / s; r = 10r mod s */ + i = temp.dp[0]; + mp_mul_d(&mplus, 10, &mplus); + mp_mul_d(&mminus, 10, &mminus); + tc1 = mp_cmp_mag(&r, &mminus); + if (lowOK) { + tc1 = (tc1 <= 0); } else { - digit = b.dp[sd]; - if (b.used > sd+1 || digit >= 10) { - Tcl_Panic("wrong digit!"); - } - --b.used; mp_clamp(&b); + tc1 = (tc1 < 0); } - - /* - * Does the current digit put us on the low side of the exact value - * but within within roundoff of being exact? - */ - - r1 = mp_cmp_mag(&b, (m2plus > m2minus)? &mplus : &mminus); - if (r1 == MP_LT - || (r1 == MP_EQ - && convType != TCL_DD_STEELE0 - && (dPtr->w.word1 & 1) == 0)) { - /* - * Make sure we shouldn't be rounding *up* instead, - * in case the next number above is closer - */ - if (ShouldBankerRoundUpPowD(&b, sd, digit&1)) { - ++digit; - if (digit == 10) { - *s++ = '9'; - s = BumpUp(s, retval, &k); - break; - } - } - - /* Stash the last digit */ - - *s++ = '0' + digit; - break; + mp_add(&r, &mplus, &temp); + tc2 = mp_cmp_mag(&temp, &s); + if (highOK) { + tc2 = (tc2 >= 0); + } else { + tc2= (tc2 > 0); } - - /* - * Does one plus the current digit put us within roundoff of the - * number? - */ - - if (ShouldBankerRoundUpToNextPowD(&b, &mminus, sd, - convType, dPtr->w.word1 & 1, - &temp)) { - if (digit == 9) { - *s++ = '9'; - s = BumpUp(s, retval, &k); + if (!tc1) { + if (!tc2) { + *buffer++ = '0' + i; + } else { + c = (char) (i + '1'); break; } - ++digit; - *s++ = '0' + digit; - break; - } - - /* - * Have we converted all the requested digits? - */ - *s++ = '0' + digit; - if (i == ilim) { - if (ShouldBankerRoundUpPowD(&b, sd, digit&1)) { - s = BumpUp(s, retval, &k); - } - break; - } - - /* Advance to the next digit */ - - mp_mul_d(&b, 10, &b); - mp_mul_d(&mminus, 10, &mminus); - if (m2plus > m2minus) { - mp_mul_2d(&mminus, m2plus-m2minus, &mplus); - } - ++i; - } - - /* - * Endgame - store the location of the decimal point and the end of the - * string. - */ - if (m2plus > m2minus) { - mp_clear(&mplus); - } - mp_clear_multi(&b, &mminus, &temp, NULL); - *s = '\0'; - *decpt = k; - if (endPtr) { - *endPtr = s; - } - return retval; -} - -/* - *----------------------------------------------------------------------------- - * - * StrictBignumConversionPowD -- - * - * Converts a double-precision number to a fixed-lengt string of - * 'ilim' digits (or 'ilim1' if log10(d) has been overestimated.) - * The denominator in David Gay's conversion algorithm is known to - * be a power of 2**DIGIT_BIT, and hence the division in the main - * loop may be replaced by a digit shift and mask. - * - * Results: - * Returns the string of significant decimal digits, in newly - * allocated memory. - * - * Side effects: - * Stores the location of the decimal point in '*decpt' and the - * location of the terminal null byte in '*endPtr'. - * - *----------------------------------------------------------------------------- - */ - -inline static char* -StrictBignumConversionPowD(Double* dPtr, - /* Original number to convert */ - int convType, - /* Type of conversion (shortest, Steele, - E format, F format) */ - Tcl_WideUInt bw, - /* Integer significand */ - int b2, int b5, - /* Scale factor for the significand - * in the numerator */ - int sd, - /* Scale factor for the denominator */ - int k, - /* Number of output digits before the decimal - * point */ - int len, - /* Number of digits to allocate */ - int ilim, - /* Number of digits to convert if b >= s */ - int ilim1, - /* Number of digits to convert if b < s */ - int* decpt, - /* OUTPUT: Position of the decimal point */ - char** endPtr) - /* OUTPUT: Position of the terminal '\0' - * at the end of the returned string */ -{ - - char* retval = ckalloc(len + 1); - /* Output buffer */ - mp_int b; /* Numerator of the fraction being converted */ - mp_digit digit; /* Current output digit */ - char* s = retval; /* Cursor in the output buffer */ - int i; /* Index in the output buffer */ - mp_int temp; - - /* - * b = bw * 2**b2 * 5**b5 - */ - - TclBNInitBignumFromWideUInt(&b, bw); - MulPow5(&b, b5, &b); - mp_mul_2d(&b, b2, &b); - - /* Adjust if the logarithm was guessed wrong */ - - if (b.used <= sd) { - mp_mul_d(&b, 10, &b); - ilim = ilim1; - --k; - } - mp_init(&temp); - - /* - * Loop through the digits. Do division and mod by s == 2**(sd*DIGIT_BIT) - * by mp_digit extraction - */ - - i = 1; - for (;;) { - if (b.used <= sd) { - digit = 0; } else { - digit = b.dp[sd]; - if (b.used > sd+1 || digit >= 10) { - Tcl_Panic("wrong digit!"); - } - --b.used; mp_clamp(&b); - } - - /* - * Have we converted all the requested digits? - */ - *s++ = '0' + digit; - if (i == ilim) { - if (ShouldBankerRoundUpPowD(&b, sd, digit&1)) { - s = BumpUp(s, retval, &k); + if (!tc2) { + c = (char) (i + '0'); } else { - while (*--s == '0') { - /* do nothing */ + mp_mul_2d(&r, 1, &r); + n = mp_cmp_mag(&r, &s); + if (n < 0) { + c = (char) (i + '0'); + } else { + c = (char) (i + '1'); } - ++s; } break; } - - /* Advance to the next digit */ - - mp_mul_d(&b, 10, &b); - ++i; - } + }; + *buffer++ = c; + *buffer++ = '\0'; - /* - * Endgame - store the location of the decimal point and the end of the - * string. + /* + * Free memory, and return. */ - mp_clear_multi(&b, &temp, NULL); - *s = '\0'; - *decpt = k; - if (endPtr) { - *endPtr = s; - } - return retval; -} - -/* - *----------------------------------------------------------------------------- - * - * ShouldBankerRoundUp -- - * - * Tests whether a digit should be rounded up or down when finishing - * bignum-based floating point conversion. - * - * Results: - * Returns 1 if the number needs to be rounded up, 0 otherwise. - * - *----------------------------------------------------------------------------- - */ -inline static int -ShouldBankerRoundUp(mp_int* twor, - /* 2x the remainder from thd division that - * produced the last digit */ - mp_int* S, /* Denominator */ - int isodd) /* Flag == 1 if the last digit is odd */ -{ - int r = mp_cmp_mag(twor, S); - switch (r) { - case MP_LT: - return 0; - case MP_EQ: - return isodd; - case MP_GT: - return 1; - } - Tcl_Panic("in ShouldBankerRoundUp, trichotomy fails!"); - return 0; + mp_clear_multi(&r, &s, &mplus, &mminus, &temp, NULL); + return k; } /* - *----------------------------------------------------------------------------- - * - * ShouldBankerRoundUpToNext -- - * - * Tests whether the remainder is great enough to force rounding - * to the next higher digit. - * - * Results: - * Returns 1 if the number should be rounded up, 0 otherwise. - * - *----------------------------------------------------------------------------- - */ - -inline static int -ShouldBankerRoundUpToNext(mp_int* b, - /* Remainder from the division that produced - * the last digit. */ - mp_int* m, - /* Numerator of the rounding tolerance */ - mp_int* S, - /* Denominator */ - int convType, - /* Conversion type: STEELE0 defeats - * round-to-even. (Not sure why one would - * want this; I coped it from Gay. FIXME */ - int isodd, - /* 1 if the integer significand is odd */ - mp_int* temp) - /* Work area needed for the calculation */ -{ - int r; - /* Compare b and S-m: this is the same as comparing B+m and S. */ - mp_add(b, m, temp); - r = mp_cmp_mag(temp, S); - switch(r) { - case MP_LT: - return 0; - case MP_EQ: - if (convType == TCL_DD_STEELE0) { - return 0; - } else { - return isodd; - } - case MP_GT: - return 1; - } - Tcl_Panic("in ShouldBankerRoundUpToNext, trichotomy fails!"); - return 0; -} - -/* - *----------------------------------------------------------------------------- + *---------------------------------------------------------------------- * - * ShorteningBignumConversion -- + * AbsoluteValue -- * - * Convert a floating point number to a variable-length digit string - * using the multiprecision method. + * Splits a 'double' into its absolute value and sign. * * Results: - * Returns the string of digits. + * Returns the absolute value. * * Side effects: - * Stores the position of the decimal point in *decpt. - * Stores a pointer to the end of the number in *endPtr. + * Stores the signum in '*signum'. * - *----------------------------------------------------------------------------- + *---------------------------------------------------------------------- */ -inline static char* -ShorteningBignumConversion(Double* dPtr, - /* Original number being converted */ - int convType, - /* Conversion type */ - Tcl_WideUInt bw, - /* Integer significand and exponent */ - int b2, - /* Scale factor for the significand */ - int m2plus, int m2minus, - /* Scale factors for 1/2 ulp in numerator */ - int s2, int s5, - /* Scale factors for denominator */ - int k, - /* Guessed position of the decimal point */ - int len, - /* Size of the digit buffer to allocate */ - int ilim, - /* Number of digits to convert if b >= s */ - int ilim1, - /* Number of digits to convert if b < s */ - int* decpt, - /* OUTPUT: Position of the decimal point */ - char** endPtr) - /* OUTPUT: Pointer to the end of the number */ +static double +AbsoluteValue( + double v, /* Number to split */ + int *signum) /* (Output) Sign of the number 1=-, 0=+ */ { - char* retval = ckalloc(len+1); - /* Buffer of digits to return */ - char* s = retval; /* Cursor in the return value */ - mp_int b; /* Numerator of the result */ - mp_int mminus; /* 1/2 ulp below the result */ - mp_int mplus; /* 1/2 ulp above the result */ - mp_int S; /* Denominator of the result */ - mp_int dig; /* Current digit of the result */ - int digit; /* Current digit of the result */ - mp_int temp; /* Work area */ - int minit = 1; /* Fudge factor for when we misguess k */ - int i; - int r1; - /* - * b = bw * 2**b2 * 5**b5 - * S = 2**s2 * 5*s5 + * 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.) */ - TclBNInitBignumFromWideUInt(&b, bw); - mp_mul_2d(&b, b2, &b); - mp_init_set_int(&S, 1); - MulPow5(&S, s5, &S); mp_mul_2d(&S, s2, &S); - - /* - * Handle the case where we guess the position of the decimal point - * wrong. - */ - - if (mp_cmp_mag(&b, &S) == MP_LT) { - mp_mul_d(&b, 10, &b); - minit = 10; - ilim =ilim1; - --k; +#ifndef IEEE_FLOATING_POINT + if (v >= 0.0) { + *signum = 0; + } else { + *signum = 1; + v = -v; } - - /* mminus = 2**m2minus * 5**m5 */ - - mp_init_set_int(&mminus, minit); - mp_mul_2d(&mminus, m2minus, &mminus); - if (m2plus > m2minus) { - mp_init_copy(&mplus, &mminus); - mp_mul_2d(&mplus, m2plus-m2minus, &mplus); +#else + union { + Tcl_WideUInt iv; + double dv; + } bitwhack; + bitwhack.dv = v; + if (n770_fp) { + bitwhack.iv = Nokia770Twiddle(bitwhack.iv); } - mp_init(&temp); - - /* Loop through the digits */ - - mp_init(&dig); - i = 1; - for (;;) { - mp_div(&b, &S, &dig, &b); - if (dig.used > 1 || dig.dp[0] >= 10) { - Tcl_Panic("wrong digit!"); - } - digit = dig.dp[0]; - - /* - * Does the current digit leave us with a remainder small enough to - * round to it? - */ - - r1 = mp_cmp_mag(&b, (m2plus > m2minus)? &mplus : &mminus); - if (r1 == MP_LT - || (r1 == MP_EQ - && convType != TCL_DD_STEELE0 - && (dPtr->w.word1 & 1) == 0)) { - mp_mul_2d(&b, 1, &b); - if (ShouldBankerRoundUp(&b, &S, digit&1)) { - ++digit; - if (digit == 10) { - *s++ = '9'; - s = BumpUp(s, retval, &k); - break; - } - } - *s++ = '0' + digit; - break; - } - - /* - * Does the current digit leave us with a remainder large enough - * to commit to rounding up to the next higher digit? - */ - - if (ShouldBankerRoundUpToNext(&b, &mminus, &S, convType, - dPtr->w.word1 & 1, &temp)) { - ++digit; - if (digit == 10) { - *s++ = '9'; - s = BumpUp(s, retval, &k); - break; - } - *s++ = '0' + digit; - break; - } - - /* Have we converted all the requested digits? */ - - *s++ = '0' + digit; - if (i == ilim) { - mp_mul_2d(&b, 1, &b); - if (ShouldBankerRoundUp(&b, &S, digit&1)) { - s = BumpUp(s, retval, &k); - } - break; - } - - /* Advance to the next digit */ - - if (s5 > 0) { - - /* Can possibly shorten the denominator */ - mp_mul_2d(&b, 1, &b); - mp_mul_2d(&mminus, 1, &mminus); - if (m2plus > m2minus) { - mp_mul_2d(&mplus, 1, &mplus); - } - mp_div_d(&S, 5, &S, NULL); - --s5; - /* - * 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 - * we shorten the denominator by at least 1 mp_digit, perhaps - * 2. as we do the conversion for 17 digits of significance. - * Possible savings: - * 10**26 1 trip through loop before fallback possible - * 10**27 1 trip - * 10**28 2 trips - * 10**29 3 trips - * 10**30 4 trips - * 10**31 5 trips - * 10**32 6 trips - * 10**33 7 trips - * 10**34 8 trips - * 10**35 9 trips - * 10**36 10 trips - * 10**37 11 trips - * 10**38 12 trips - * 10**39 13 trips - * 10**40 14 trips - * 10**41 15 trips - * 10**42 16 trips - * thereafter no gain. - */ - } else { - mp_mul_d(&b, 10, &b); - mp_mul_d(&mminus, 10, &mminus); - if (m2plus > m2minus) { - mp_mul_2d(&mplus, 10, &mplus); - } + if (bitwhack.iv & ((Tcl_WideUInt) 1 << 63)) { + *signum = 1; + bitwhack.iv &= ~((Tcl_WideUInt) 1 << 63); + if (n770_fp) { + bitwhack.iv = Nokia770Twiddle(bitwhack.iv); } - - ++i; - } - - - /* - * Endgame - store the location of the decimal point and the end of the - * string. - */ - if (m2plus > m2minus) { - mp_clear(&mplus); - } - mp_clear_multi(&b, &mminus, &temp, &dig, &S, NULL); - *s = '\0'; - *decpt = k; - if (endPtr) { - *endPtr = s; + v = bitwhack.dv; + } else { + *signum = 0; } - return retval; - +#endif + return v; } - + /* - *----------------------------------------------------------------------------- + *---------------------------------------------------------------------- * - * StrictBignumConversion -- + * GetIntegerTimesPower -- * - * Convert a floating point number to a fixed-length digit string - * using the multiprecision method. + * Converts a floating point number to an exact integer times a power of + * the floating point radix. * * Results: - * Returns the string of digits. + * Returns 1 if it converted the smallest significand, 0 otherwise. * * Side effects: - * Stores the position of the decimal point in *decpt. - * Stores a pointer to the end of the number in *endPtr. + * Initializes the integer value (does not just assign it), and stores + * the exponent. * - *----------------------------------------------------------------------------- + *---------------------------------------------------------------------- */ -inline static char* -StrictBignumConversion(Double* dPtr, - /* Original number being converted */ - int convType, - /* Conversion type */ - Tcl_WideUInt bw, - /* Integer significand and exponent */ - int b2, /* Scale factor for the significand */ - int s2, int s5, - /* Scale factors for denominator */ - int k, /* Guessed position of the decimal point */ - int len, /* Size of the digit buffer to allocate */ - int ilim, - /* Number of digits to convert if b >= s */ - int ilim1, - /* Number of digits to convert if b < s */ - int* decpt, - /* OUTPUT: Position of the decimal point */ - char** endPtr) - /* OUTPUT: Pointer to the end of the number */ +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*/ { - char* retval = ckalloc(len+1); - /* Buffer of digits to return */ - char* s = retval; /* Cursor in the return value */ - mp_int b; /* Numerator of the result */ - mp_int S; /* Denominator of the result */ - mp_int dig; /* Current digit of the result */ - int digit; /* Current digit of the result */ - mp_int temp; /* Work area */ - int g; /* Size of the current digit groun */ - int i, j; - - /* - * b = bw * 2**b2 * 5**b5 - * S = 2**s2 * 5*s5 - */ - - mp_init_multi(&temp, &dig, NULL); - TclBNInitBignumFromWideUInt(&b, bw); - mp_mul_2d(&b, b2, &b); - mp_init_set_int(&S, 1); - MulPow5(&S, s5, &S); mp_mul_2d(&S, s2, &S); + double a, f; + int e, i, n; /* - * Handle the case where we guess the position of the decimal point - * wrong. - */ - - if (mp_cmp_mag(&b, &S) == MP_LT) { - mp_mul_d(&b, 10, &b); - ilim =ilim1; - --k; - } - - /* Convert the leading digit */ - - i = 0; - mp_div(&b, &S, &dig, &b); - if (dig.used > 1 || dig.dp[0] >= 10) { - Tcl_Panic("wrong digit!"); - } - digit = dig.dp[0]; - - /* Is a single digit all that was requested? */ - - *s++ = '0' + digit; - if (++i >= ilim) { - mp_mul_2d(&b, 1, &b); - if (ShouldBankerRoundUp(&b, &S, digit&1)) { - s = BumpUp(s, retval, &k); - } - } else { - - for (;;) { - - /* Shift by a group of digits. */ - - g = ilim - i; - if (g > DIGIT_GROUP) { - g = DIGIT_GROUP; - } - if (s5 >= g) { - mp_div_d(&S, dpow5[g], &S, NULL); - s5 -= g; - } else if (s5 > 0) { - mp_div_d(&S, dpow5[s5], &S, NULL); - mp_mul_d(&b, dpow5[g - s5], &b); - s5 = 0; - } else { - mp_mul_d(&b, dpow5[g], &b); - } - mp_mul_2d(&b, g, &b); - - /* - * As with the shortening bignum conversion, it's possible at - * this point that we will have reduced the denominator to - * less than 2**64/10, at which point it would be possible to - * fall back to to int64 arithmetic. But the potential payoff - * is tremendously less - unless we're working in F format - - * because we know that three groups of digits will always - * suffice for %#.17e, the longest format that doesn't introduce - * empty precision. - */ - - /* Extract the next group of digits */ - - mp_div(&b, &S, &dig, &b); - if (dig.used > 1) { - Tcl_Panic("wrong digit!"); - } - digit = dig.dp[0]; - for (j = g-1; j >= 0; --j) { - int t = itens[j]; - *s++ = digit / t + '0'; - digit %= t; - } - i += g; - - /* Have we converted all the requested digits? */ - - if (i == ilim) { - mp_mul_2d(&b, 1, &b); - if (ShouldBankerRoundUp(&b, &S, digit&1)) { - s = BumpUp(s, retval, &k); - } else { - while (*--s == '0') { - /* do nothing */ - } - ++s; - } - break; - } - } - } - /* - * Endgame - store the location of the decimal point and the end of the - * string. - */ - mp_clear_multi(&b, &S, &temp, &dig, NULL); - *s = '\0'; - *decpt = k; - if (endPtr) { - *endPtr = s; - } - return retval; - -} - -/* - *----------------------------------------------------------------------------- - * - * TclDoubleDigits -- - * - * Core of Tcl's conversion of double-precision floating point numbers - * to decimal. - * - * Results: - * Returns a newly-allocated string of digits. - * - * Side effects: - * Sets *decpt to the index of the character in the string before the - * place that the decimal point should go. If 'endPtr' is not NULL, - * sets endPtr to point to the terminating '\0' byte of the string. - * Sets *sign to 1 if a minus sign should be printed with the number, - * or 0 if a plus sign (or no sign) should appear. - * - * This function is a service routine that produces the string of digits - * for floating-point-to-decimal conversion. It can do a number of things - * according to the 'flags' argument. Valid values for 'flags' include: - * TCL_DD_SHORTEST - This is the default for floating point conversion - * if ::tcl_precision is 0. It constructs the shortest string - * of digits that will reconvert to the given number when scanned. - * For floating point numbers that are exactly between two - * decimal numbers, it resolves using the 'round to even' rule. - * With this value, the 'ndigits' parameter is ignored. - * TCL_DD_STEELE - This value is not recommended and may be removed - * in the future. It follows the conversion algorithm outlined - * in "How to Print Floating-Point Numbers Accurately" by - * Guy L. Steele, Jr. and Jon L. White [Proc. ACM SIGPLAN '90, - * pp. 112-126]. This rule has the effect of rendering 1e23 - * as 9.9999999999999999e22 - which is a 'better' approximation - * in the sense that it will reconvert correctly even if - * a subsequent input conversion is 'round up' or 'round down' - * rather than 'round to nearest', but is surprising otherwise. - * TCL_DD_E_FORMAT - This value is used to prepare numbers for %e - * format conversion (or for default floating->string if - * tcl_precision is not 0). It constructs a string of at most - * 'ndigits' digits, choosing the one that is closest to the - * 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 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. - * It is possible for this format to result in a zero-length - * string if the number is sufficiently small. Again, it - * is permissible for TCL_DD_F_FORMAT to return fewer digits - * for a number that converts exactly, and changing the - * 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, - * E and F format with fewer than about 14 digits will be done with - * a quick floating point approximation and fall back on the exact - * arithmetic only if the input number is close enough to the - * midpoint between two decimal strings that more precision is needed - * to resolve which string is correct. - * - * The value stored in the 'decpt' argument on return may be negative - * (indicating that the decimal point falls to the left of the string) - * or greater than the length of the string. In addition, the value -9999 - * is used as a sentinel to indicate that the string is one of the special - * values "Infinity" and "NaN", and that no decimal point should be inserted. - * - *----------------------------------------------------------------------------- - */ -char* -TclDoubleDigits(double dv, /* Number to convert */ - int ndigits, /* Number of digits requested */ - int flags, /* Conversion flags */ - int* decpt, /* OUTPUT: Position of the decimal point */ - int* sign, /* OUTPUT: 1 if the result is negative */ - char** endPtr) /* OUTPUT: If not NULL, receives a pointer - * to one character beyond the end - * of the returned string */ -{ - int convType = (flags & TCL_DD_CONVERSION_TYPE_MASK); - /* Type of conversion being performed - * TCL_DD_SHORTEST0 - * TCL_DD_STEELE0 - * TCL_DD_E_FORMAT - * TCL_DD_F_FORMAT */ - Double d; /* Union for deconstructing doubles */ - Tcl_WideUInt bw; /* Integer significand */ - int be; /* Power of 2 by which b must be multiplied */ - int bbits; /* Number of bits needed to represent b */ - int denorm; /* Flag == 1 iff the input number was - * denormalized */ - int k; /* Estimate of floor(log10(d)) */ - int k_check; /* Flag == 1 if d is near enough to a - * power of ten that k must be checked */ - int b2, b5, s2, s5; /* Powers of 2 and 5 in the numerator and - * denominator of intermediate results */ - int ilim = -1, ilim1 = -1; /* Number of digits to convert, and number - * to convert if log10(d) has been - * overestimated */ - char* retval; /* Return value from this function */ - int i = -1; - - /* Put the input number into a union for bit-whacking */ - - d.d = dv; - - /* - * Handle the cases of negative numbers (by taking the absolute value: - * this includes -Inf and -NaN!), infinity, Not a Number, and zero. + * Develop f and e such that v = f * FLT_RADIX**e, with + * 1.0/FLT_RADIX <= f < 1. */ - TakeAbsoluteValue(&d, sign); - if ((d.w.word0 & EXP_MASK) == EXP_MASK) { - return FormatInfAndNaN(&d, decpt, endPtr); + 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); } - if (d.d == 0.0) { - return FormatZero(decpt, endPtr); + e = (e - n) / log2FLT_RADIX; +#endif + if (f == 1.0) { + f = 1.0 / FLT_RADIX; + e += 1; } - /* - * Unpack the floating point into a wide integer and an exponent. - * Determine the number of bits that the big integer requires, and - * compute a quick approximation (which may be one too high) of - * ceil(log10(d.d)). - */ - denorm = ((d.w.word0 & EXP_MASK) == 0); - DoubleToExpAndSig(d.d, &bw, &be, &bbits); - k = ApproximateLog10(bw, be, bbits); - k = BetterLog10(d.d, k, &k_check); - - /* At this point, we have: - * d is the number to convert. - * bw are significand and exponent: d == bw*2**be, - * bbits is the length of bw: 2**bbits-1 <= bw < 2**bbits - * k is either ceil(log10(d)) or ceil(log10(d))+1. k_check is 0 - * if we know that k is exactly ceil(log10(d)) and 1 if we need to - * check. - * We want a rational number - * r = b * 10**(1-k) = bw * 2**b2 * 5**b5 / (2**s2 / 5**s5), - * with b2, b5, s2, s5 >= 0. Note that the most significant decimal - * digit is floor(r) and that successive digits can be obtained - * by setting r <- 10*floor(r) (or b <= 10 * (b % S)). - * Find appropriate b2, b5, s2, s5. - */ - - ComputeScale(be, k, &b2, &b5, &s2, &s5); - /* - * Correct an incorrect caller-supplied 'ndigits'. - * Also determine: - * i = The maximum number of decimal digits that will be returned in the - * formatted string. This is k + 1 + ndigits for F format, 18 for - * shortest and Steele, and ndigits for E format. - * ilim = The number of significant digits to convert if - * k has been guessed correctly. This is -1 for shortest and Steele - * (which stop when all significance has been lost), 'ndigits' - * for E format, and 'k + 1 + ndigits' for F format. - * ilim1 = The minimum number of significant digits to convert if - * k has been guessed 1 too high. This, too, is -1 for shortest - * and Steele, and 'ndigits' for E format, but it's 'ndigits-1' - * for F format. + * If the original number was denormalized, adjust e and f to be denormal + * as well. */ - SetPrecisionLimits(convType, k, &ndigits, &i, &ilim, &ilim1); - - /* - * Try to do low-precision conversion in floating point rather - * than resorting to expensive multiprecision arithmetic - */ - if (ilim >= 0 && ilim <= QUICK_MAX && !(flags & TCL_DD_NO_QUICK)) { - if ((retval = QuickConversion(d.d, k, k_check, flags, - i, ilim, ilim1, - decpt, endPtr)) != NULL) { - return retval; - } - } - - /* - * For shortening conversions, determine the upper and lower bounds - * for the remainder at which we can stop. - * m+ = (2**m2plus * 5**m5) / (2**s2 * 5**s5) is the limit on the - * high side, and - * m- = (2**m2minus * 5**m5) / (2**s2 * 5**s5) is the limit on the - * low side. - * We may need to increase s2 to put m2plus, m2minus, b2 over a - * common denominator. - */ - - if (flags & TCL_DD_SHORTEN_FLAG) { - int m2minus = b2; - int m2plus; - int m5 = b5; - int len = i; - - /* - * Find the quantity i so that (2**i*5**b5)/(2**s2*5**s5) - * is 1/2 unit in the least significant place of the floating - * point number. - */ - if (denorm) { - i = be + EXPONENT_BIAS + (FP_PRECISION-1); - } else { - i = 1 + FP_PRECISION - bbits; - } - b2 += i; - s2 += i; - - /* - * Reduce the fractions to lowest terms, since the above calculation - * may have left excess powers of 2 in numerator and denominator - */ - CastOutPowersOf2(&b2, &m2minus, &s2); - - /* - * In the special case where bw==1, the nearest floating point number - * to it on the low side is 1/4 ulp below it. Adjust accordingly. - */ - m2plus = m2minus; - if (!denorm && bw == 1) { - ++b2; - ++s2; - ++m2plus; - } - - if (s5+1 < N_LOG2POW5 - && s2+1 + log2pow5[s5+1] <= 64) { - /* - * If 10*2**s2*5**s5 == 2**(s2+1)+5**(s5+1) fits in a 64-bit - * word, then all our intermediate calculations can be done - * using exact 64-bit arithmetic with no need for expensive - * multiprecision operations. (This will be true for all numbers - * in the range [1.0e-3 .. 1.0e+24]). - */ - - return ShorteningInt64Conversion(&d, convType, bw, b2, b5, - m2plus, m2minus, m5, - s2, s5, k, len, ilim, ilim1, - decpt, endPtr); - } else if (s5 == 0) { - /* - * The denominator is a power of 2, so we can replace division - * by digit shifts. First we round up s2 to a multiple of - * DIGIT_BIT, and adjust m2 and b2 accordingly. Then we launch - * into a version of the comparison that's specialized for - * the 'power of mp_digit in the denominator' case. - */ - if (s2 % DIGIT_BIT != 0) { - int delta = DIGIT_BIT - (s2 % DIGIT_BIT); - b2 += delta; - m2plus += delta; - m2minus += delta; - s2 += delta; - } - return ShorteningBignumConversionPowD(&d, convType, bw, b2, b5, - m2plus, m2minus, m5, - s2/DIGIT_BIT, k, len, - ilim, ilim1, decpt, endPtr); - } else { - - /* - * Alas, there's no helpful special case; use full-up - * bignum arithmetic for the conversion - */ - - return ShorteningBignumConversion(&d, convType, bw, - b2, m2plus, m2minus, - s2, s5, k, len, - ilim, ilim1, decpt, endPtr); - - } - + 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; + } - /* Non-shortening conversion */ - - int len = i; - - /* Reduce numerator and denominator to lowest terms */ - - if (b2 >= s2 && s2 > 0) { - b2 -= s2; s2 = 0; - } else if (s2 >= b2 && b2 > 0) { - s2 -= b2; b2 = 0; - } - - if (s5+1 < N_LOG2POW5 - && s2+1 + log2pow5[s5+1] <= 64) { - /* - * If 10*2**s2*5**s5 == 2**(s2+1)+5**(s5+1) fits in a 64-bit - * word, then all our intermediate calculations can be done - * using exact 64-bit arithmetic with no need for expensive - * multiprecision operations. - */ - - return StrictInt64Conversion(&d, convType, bw, b2, b5, - s2, s5, k, len, ilim, ilim1, - decpt, endPtr); - - } else if (s5 == 0) { - /* - * The denominator is a power of 2, so we can replace division - * by digit shifts. First we round up s2 to a multiple of - * DIGIT_BIT, and adjust m2 and b2 accordingly. Then we launch - * into a version of the comparison that's specialized for - * the 'power of mp_digit in the denominator' case. - */ - if (s2 % DIGIT_BIT != 0) { - int delta = DIGIT_BIT - (s2 % DIGIT_BIT); - b2 += delta; - s2 += delta; - } - return StrictBignumConversionPowD(&d, convType, bw, b2, b5, - s2/DIGIT_BIT, k, len, - ilim, ilim1, decpt, endPtr); - } else { - /* - * There are no helpful special cases, but at least we know - * in advance how many digits we will convert. We can run the - * conversion in steps of DIGIT_GROUP digits, so as to - * have many fewer mp_int divisions. - */ - return StrictBignumConversion(&d, convType, bw, b2, s2, s5, - k, len, ilim, ilim1, decpt, endPtr); - } - } + /* + * 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); } - /* *---------------------------------------------------------------------- @@ -4324,7 +2174,7 @@ TclInitDoubleConversion(void) } bitwhack; #endif -#if defined(__sgi) && defined(_COMPILER_VERSION) +#if defined(__mips) union fpc_csr mipsCR; mipsCR.fc_word = get_fpc_csr(); @@ -4355,7 +2205,7 @@ TclInitDoubleConversion(void) if (frexp((double) FLT_RADIX, &log2FLT_RADIX) != 0.5) { Tcl_Panic("This code doesn't work on a decimal machine!"); } - log2FLT_RADIX--; + --log2FLT_RADIX; mantBits = DBL_MANT_DIG * log2FLT_RADIX; d = 1.0; @@ -4386,11 +2236,6 @@ TclInitDoubleConversion(void) for (i=0; i<8; ++i) { mp_sqr(pow5+i, pow5+i+1); } - mp_init_set_int(pow5_13, 1220703125); - for (i = 1; i < 5; ++i) { - mp_init(pow5_13 + i); - mp_sqr(pow5_13 + i - 1, pow5_13 + i); - } /* * Determine the number of decimal digits to the left and right of the @@ -4399,10 +2244,12 @@ TclInitDoubleConversion(void) * 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.)); /* @@ -4446,7 +2293,7 @@ TclFinalizeDoubleConversion(void) { int i; - ckfree((char *) pow10_wide); + Tcl_Free((char *) pow10_wide); for (i=0; i<9; ++i) { mp_clear(pow5 + i); } @@ -4531,13 +2378,12 @@ TclBignumToDouble( mp_int *a) /* Integer to convert. */ { mp_int b; - int bits, shift, i, lsb; + int bits, shift, i; double r; - /* - * We need a 'mantBits'-bit significand. Determine what shift will - * give us that. + * 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); @@ -4549,54 +2395,17 @@ TclBignumToDouble( return -HUGE_VAL; } } - shift = mantBits - bits; - - /* - * If shift > 0, shift the significand left by the requisite number of - * bits. If shift == 0, the significand is already exactly 'mantBits' - * in length. If shift < 0, we will need to shift the significand right - * by the requisite number of bits, and round it. If the '1-shift' - * least significant bits are 0, but the 'shift'th bit is nonzero, - * then the significand lies exactly between two values and must be - * 'rounded to even'. - */ - + shift = mantBits + 1 - bits; mp_init(&b); - if (shift == 0) { - mp_copy(a, &b); - } else if (shift > 0) { + if (shift > 0) { mp_mul_2d(a, shift, &b); } else if (shift < 0) { - lsb = mp_cnt_lsb(a); - if (lsb == -1-shift) { - - /* - * Round to even - */ - - mp_div_2d(a, -shift, &b, NULL); - if (mp_isodd(&b)) { - if (b.sign == MP_ZPOS) { - mp_add_d(&b, 1, &b); - } else { - mp_sub_d(&b, 1, &b); - } - } - } else { - - /* - * Ordinary rounding - */ - - mp_div_2d(a, -1-shift, &b, NULL); - if (b.sign == MP_ZPOS) { - mp_add_d(&b, 1, &b); - } else { - mp_sub_d(&b, 1, &b); - } - mp_div_2d(&b, 1, &b, NULL); - } + mp_div_2d(a, -shift, &b, NULL); + } else { + mp_copy(a, &b); } + mp_add_d(&b, 1, &b); + mp_div_2d(&b, 1, &b, NULL); /* * Accumulate the result, one mp_digit at a time. @@ -4625,20 +2434,6 @@ TclBignumToDouble( } } -/* - *----------------------------------------------------------------------------- - * - * TclCeil -- - * - * Computes the smallest floating point number that is at least the - * mp_int argument. - * - * Results: - * Returns the floating point number. - * - *----------------------------------------------------------------------------- - */ - double TclCeil( mp_int *a) /* Integer to convert. */ @@ -4682,20 +2477,6 @@ TclCeil( return r; } -/* - *----------------------------------------------------------------------------- - * - * TclFloor -- - * - * Computes the largest floating point number less than or equal to - * the mp_int argument. - * - * Results: - * Returns the floating point value. - * - *----------------------------------------------------------------------------- - */ - double TclFloor( mp_int *a) /* Integer to convert. */ |