From 1f81e5eb51a6f1fea57a23736af10772fc6abfae Mon Sep 17 00:00:00 2001 From: dgp Date: Sat, 21 Dec 2019 14:19:08 +0000 Subject: Tests demonstrating the number parsing overflow bugs. --- tests/expr.test | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/tests/expr.test b/tests/expr.test index 3a69407..948195b 100644 --- a/tests/expr.test +++ b/tests/expr.test @@ -6836,6 +6836,56 @@ test expr-41.2 {exponent underflow} { expr 1.0e-2147483630 } 0.0 +test expr-41.3 {exponent overflow} { + expr 1e2147483647 +} Inf +test expr-41.4 {exponent overflow} { + expr 1e2147483648 +} Inf +test expr-41.5 {exponent overflow} { + expr 100e2147483645 +} Inf +test expr-41.6 {exponent overflow} { + expr 100e2147483646 +} Inf +test expr-41.7 {exponent overflow} { + expr 1.0e2147483647 +} Inf +test expr-41.8 {exponent overflow} { + expr 1.0e2147483648 +} Inf +test expr-41.9 {exponent overflow} { + expr 1.2e2147483647 +} Inf +test expr-41.10 {exponent overflow} { + expr 1.2e2147483648 +} Inf + +test expr-41.11 {exponent overflow} { + expr 1e-2147483648 +} 0.0 +test expr-41.12 {exponent overflow} { + expr 1e-2147483649 +} 0.0 +test expr-41.13 {exponent overflow} { + expr 100e-2147483650 +} 0.0 +test expr-41.14 {exponent overflow} { + expr 100e-2147483651 +} 0.0 +test expr-41.15 {exponent overflow} { + expr 1.0e-2147483648 +} 0.0 +test expr-41.16 {exponent overflow} { + expr 1.0e-2147483649 +} 0.0 +test expr-41.17 {exponent overflow} { + expr 1.23e-2147483646 +} 0.0 +test expr-41.18 {exponent overflow} { + expr 1.23e-2147483647 +} 0.0 + test expr-42.1 {denormals} ieeeFloatingPoint { expr 7e-324 } 5e-324 -- cgit v0.12 From e52a22ff57e01e8754a81d91e63d00815a68f206 Mon Sep 17 00:00:00 2001 From: dgp Date: Sun, 22 Dec 2019 18:44:45 +0000 Subject: Tests for another parsing bug. --- tests/expr.test | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/expr.test b/tests/expr.test index 948195b..ae86473 100644 --- a/tests/expr.test +++ b/tests/expr.test @@ -6886,6 +6886,13 @@ test expr-41.18 {exponent overflow} { expr 1.23e-2147483647 } 0.0 +test expr-41.19 {numSigDigs == 0} { + expr 0e309 +} 0.0 +test expr-41.19 {numSigDigs == 0} { + expr 0e310 +} 0.0 + test expr-42.1 {denormals} ieeeFloatingPoint { expr 7e-324 } 5e-324 -- cgit v0.12 From 38d92076278cb27c34225e14c8240efe720ca431 Mon Sep 17 00:00:00 2001 From: dgp Date: Sun, 22 Dec 2019 23:48:00 +0000 Subject: Assign a double literal to a double variable. No point in requiring conversion. --- generic/tclStrToD.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/generic/tclStrToD.c b/generic/tclStrToD.c index a93c81b..e8cd416 100644 --- a/generic/tclStrToD.c +++ b/generic/tclStrToD.c @@ -1650,7 +1650,7 @@ MakeHighPrecisionDouble( goto returnValue; } if (numSigDigs+exponent-1 < minDigits) { - retval = 0; + retval = 0.0; goto returnValue; } -- cgit v0.12 From 63253bc07d07f87120a7414708edc4dee2e46884 Mon Sep 17 00:00:00 2001 From: dgp Date: Mon, 23 Dec 2019 00:54:44 +0000 Subject: Fix parsing bug when (numSigDigs == 0). --- generic/tclStrToD.c | 8 ++++---- tests/expr.test | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/generic/tclStrToD.c b/generic/tclStrToD.c index e8cd416..bb83885 100644 --- a/generic/tclStrToD.c +++ b/generic/tclStrToD.c @@ -1645,12 +1645,12 @@ MakeHighPrecisionDouble( * Quick checks for over/underflow. */ - if (numSigDigs+exponent-1 > maxDigits) { - retval = HUGE_VAL; + if ((numSigDigs == 0) || (numSigDigs-1+exponent < minDigits)) { + retval = 0.0; goto returnValue; } - if (numSigDigs+exponent-1 < minDigits) { - retval = 0.0; + if (numSigDigs-1+exponent > maxDigits) { + retval = HUGE_VAL; goto returnValue; } diff --git a/tests/expr.test b/tests/expr.test index ae86473..a15398f 100644 --- a/tests/expr.test +++ b/tests/expr.test @@ -6889,7 +6889,7 @@ test expr-41.18 {exponent overflow} { test expr-41.19 {numSigDigs == 0} { expr 0e309 } 0.0 -test expr-41.19 {numSigDigs == 0} { +test expr-41.20 {numSigDigs == 0} { expr 0e310 } 0.0 -- cgit v0.12 From ba0a3a3dd2b77bffb9947f43a7b38f8e27e20868 Mon Sep 17 00:00:00 2001 From: Kevin B Kenny Date: Thu, 26 Dec 2019 23:54:03 +0000 Subject: Add test cases that used to cause floating point overflow in computing the correction term in floating point input conversion. Fix exponent overflow in floating point input conversion, and floating-point overflow in the significand in input conversion. --- generic/tclStrToD.c | 132 ++++++++++++++++++++++++++++++++++++++++------------ tests/expr.test | 9 ++++ 2 files changed, 110 insertions(+), 31 deletions(-) diff --git a/generic/tclStrToD.c b/generic/tclStrToD.c index bb83885..a535aed 100644 --- a/generic/tclStrToD.c +++ b/generic/tclStrToD.c @@ -266,10 +266,10 @@ static const Tcl_WideUInt wuipow5[27] = { static int AccumulateDecimalDigit(unsigned, int, Tcl_WideUInt *, mp_int *, int); static double MakeHighPrecisionDouble(int signum, - mp_int *significand, int nSigDigs, int exponent); + mp_int *significand, int nSigDigs, long exponent); static double MakeLowPrecisionDouble(int signum, Tcl_WideUInt significand, int nSigDigs, - int exponent); + long exponent); static double MakeNaN(int signum, Tcl_WideUInt tag); static double RefineApproximation(double approx, mp_int *exactSignificand, int exponent); @@ -1298,16 +1298,45 @@ TclParseNumber( objPtr->typePtr = &tclDoubleType; if (exponentSignum) { + /* + * At this point exponent>=0, so the following calculation + * cannot underflow. + */ exponent = - exponent; } + + /* + * Adjust the exponent for the number of trailing zeros that + * have not been accumulated, and the number of digits after + * the decimal point. Pin any overflow to LONG_MAX/LONG_MIN + * respectively. + */ + + if (exponent >= 0) { + if (exponent - numDigitsAfterDp > LONG_MAX - numTrailZeros) { + exponent = LONG_MAX; + } else { + exponent = exponent - numDigitsAfterDp + numTrailZeros; + } + } else { + if (exponent + numTrailZeros < LONG_MIN + numDigitsAfterDp) { + exponent = LONG_MIN; + } else { + exponent = exponent + numTrailZeros - numDigitsAfterDp; + } + } + + /* + * The desired number is now significandWide * 10**exponent + * or significandBig * 10**exponent, depending on whether + * the significand has overflowed a wide int. + */ if (!significandOverflow) { objPtr->internalRep.doubleValue = MakeLowPrecisionDouble( - signum, significandWide, numSigDigs, - (numTrailZeros + exponent - numDigitsAfterDp)); + signum, significandWide, numSigDigs, exponent); } else { objPtr->internalRep.doubleValue = MakeHighPrecisionDouble( - signum, &significandBig, numSigDigs, - (numTrailZeros + exponent - numDigitsAfterDp)); + signum, &significandBig, numSigDigs, exponent); } break; @@ -1494,7 +1523,7 @@ 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 */ + long exponent) /* Power of ten */ { double retval; /* Value of the number */ mp_int significandBig; /* Significand expressed as a bignum */ @@ -1521,6 +1550,9 @@ MakeLowPrecisionDouble( * Test for the easy cases. */ + if (significand == 0) { + return copysign(0.0, -signum); + } if (numSigDigs <= QUICK_MAX) { if (exponent >= 0) { if (exponent <= mmaxpow) { @@ -1618,7 +1650,7 @@ 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 */ + long exponent) /* Power of 10 by which to multiply */ { double retval; int machexp; /* Machine exponent of a power of 10 */ @@ -1642,16 +1674,19 @@ MakeHighPrecisionDouble( #endif /* - * Quick checks for over/underflow. + * Quick checks for zero, and over/underflow. Be careful to avoid + * integer overflow when calculating with 'exponent'. */ - if ((numSigDigs == 0) || (numSigDigs-1+exponent < minDigits)) { - retval = 0.0; - goto returnValue; + if (mp_iszero(significand)) { + return copysign(0.0, -signum); } - if (numSigDigs-1+exponent > maxDigits) { + if (exponent >= 0 && exponent-1 > maxDigits-numSigDigs) { retval = HUGE_VAL; goto returnValue; + } else if (exponent < 0 && numSigDigs+exponent < minDigits+1) { + retval = 0.0; + goto returnValue; } /* @@ -1789,6 +1824,9 @@ RefineApproximation( * "round to even" functionality */ double rteSignificand; /* Significand of the round-to-even result */ int rteExponent; /* Exponent of the round-to-even result */ + int shift; /* Shift count for converting numerator + * and denominator of corrector to floating + * point */ Tcl_WideInt rteSigWide; /* Wide integer version of the significand * for testing evenness */ int i; @@ -1801,13 +1839,22 @@ RefineApproximation( if (approxResult == HUGE_VAL) { return approxResult; } + significand = frexp(approxResult, &binExponent); /* - * Find a common denominator for the decimal and binary fractions. The - * common denominator will be 2**M2 + 5**M5. + * We are trying to compute a corrector term that, when added to the + * approximate result, will yield close to the exact result. + * The exact result is exactSignificand * 10**exponent. + * The approximate result is significand * 2**binExponent + * If exponent<0, we need to multiply the exact value by 10**-exponent + * to make it an integer, plus another factor of 2 to decide on rounding. + * Similarly if binExponent 0) { + mp_div_2d(&twoMv, shift, &twoMv, NULL); + mp_div_2d(&twoMd, shift, &twoMd, NULL); + } + /* * Convert the numerator and denominator of the corrector term accurately * to floating point numbers. diff --git a/tests/expr.test b/tests/expr.test index a15398f..7493663 100644 --- a/tests/expr.test +++ b/tests/expr.test @@ -6892,6 +6892,15 @@ test expr-41.19 {numSigDigs == 0} { test expr-41.20 {numSigDigs == 0} { expr 0e310 } 0.0 +test expr-41.21 {negative zero, large exponent} { + expr -0e309 +} -0.0 +test expr-41.22 {negative zero, large exponent} { + expr -0e310 +} -0.0 +test expr-41.23 {floating point overflow on significand (Bug 1de6b0629e)} { + expr 123[string repeat 0 309]1e-310 +} 123.0 test expr-42.1 {denormals} ieeeFloatingPoint { expr 7e-324 -- cgit v0.12