From c1af880cfe49e05ba4dd7d2607ba3715f1ff87c3 Mon Sep 17 00:00:00 2001 From: Kevin B Kenny Date: Fri, 15 Jul 2022 17:11:41 +0000 Subject: Restore FP control word on conversion of zero values. Sneak path existed that failed to restore the floating point control word when scanning a zero value. The result was that floating point rounding was incorrectly left set to 53-bit significance, round-to-even, which is incompatible with the math library from musl. --- generic/tclStrToD.c | 42 +++++++++++++++++++++++++++++------------- 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/generic/tclStrToD.c b/generic/tclStrToD.c index 61162d0..efff815 100644 --- a/generic/tclStrToD.c +++ b/generic/tclStrToD.c @@ -1627,7 +1627,6 @@ MakeLowPrecisionDouble( int numSigDigs, /* Number of digits in the significand */ long exponent) /* Power of ten */ { - double retval; /* Value of the number. */ mp_int significandBig; /* Significand expressed as a bignum. */ /* @@ -1635,18 +1634,25 @@ MakeLowPrecisionDouble( * This causes the result of double-precision calculations to be rounded * twice: once to the precision of double-extended and then again to the * precision of double. Double-rounding introduces gratuitous errors of 1 - * ulp, so we need to change rounding mode to 53-bits. + * ulp, so we need to change rounding mode to 53-bits. We also make + * 'retval' volatile, so that it doesn't get promoted to a register. */ - - TCL_IEEE_DOUBLE_ROUNDING; + volatile double retval; /* Value of the number. */ /* - * Test for the easy cases. + * Test for zero significand, which requires explicit construction + * of -0.0. (Unary minus returns a positive zero.) */ - if (significand == 0) { return copysign(0.0, -signum); } + + /* + * Set the FP control word for 53 bits, WARNING: It must be reset + * before returning. + */ + TCL_IEEE_DOUBLE_ROUNDING; + if (numSigDigs <= QUICK_MAX) { if (exponent >= 0) { if (exponent <= mmaxpow) { @@ -1744,7 +1750,6 @@ MakeHighPrecisionDouble( int numSigDigs, /* Number of significant digits */ long exponent) /* Power of 10 by which to multiply */ { - double retval; int machexp; /* Machine exponent of a power of 10. */ /* @@ -1752,19 +1757,30 @@ MakeHighPrecisionDouble( * This causes the result of double-precision calculations to be rounded * twice: once to the precision of double-extended and then again to the * precision of double. Double-rounding introduces gratuitous errors of 1 - * ulp, so we need to change rounding mode to 53-bits. + * ulp, so we need to change rounding mode to 53-bits. We also make + * 'retval' volatile to make sure that it doesn't get promoted to a + * register. */ + volatile double retval; + /* + * A zero significand requires explicit construction of -0.0. + * (Unary minus returns positive zero.) + */ + if (mp_iszero(significand)) { + return copysign(0.0, -signum); + } + + /* + * Set the 53-bit rounding mode. WARNING: It must be reset before + * returning. + */ TCL_IEEE_DOUBLE_ROUNDING; /* - * Quick checks for zero, and over/underflow. Be careful to avoid + * Make quick checks for over/underflow. Be careful to avoid * integer overflow when calculating with 'exponent'. */ - - if (mp_iszero(significand)) { - return copysign(0.0, -signum); - } if (exponent >= 0 && exponent-1 > maxDigits-numSigDigs) { retval = HUGE_VAL; goto returnValue; -- cgit v0.12