diff options
author | jan.nijtmans <nijtmans@users.sourceforge.net> | 2022-07-17 13:03:42 (GMT) |
---|---|---|
committer | jan.nijtmans <nijtmans@users.sourceforge.net> | 2022-07-17 13:03:42 (GMT) |
commit | fb1fd5d59f34d2ac112093399fdd1487d8978981 (patch) | |
tree | 2869252d5d2186be9ab011f550d88ad7f0c97206 /generic | |
parent | 505c18d593b11fa682694a7653b17b325c1aac0e (diff) | |
parent | 9e8528863dde14a2236d2cc2b012254a77418753 (diff) | |
download | tcl-fb1fd5d59f34d2ac112093399fdd1487d8978981.zip tcl-fb1fd5d59f34d2ac112093399fdd1487d8978981.tar.gz tcl-fb1fd5d59f34d2ac112093399fdd1487d8978981.tar.bz2 |
Fix [713653b951]: Floating point precision problems on x86 musl
Diffstat (limited to 'generic')
-rw-r--r-- | generic/tclStrToD.c | 42 |
1 files changed, 29 insertions, 13 deletions
diff --git a/generic/tclStrToD.c b/generic/tclStrToD.c index 61162d0..375746d 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. */ - - TCL_IEEE_DOUBLE_ROUNDING; + volatile double retval; /* - * Quick checks for zero, and over/underflow. Be careful to avoid - * integer overflow when calculating with 'exponent'. + * 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; + + /* + * Make quick checks for over/underflow. Be careful to avoid + * integer overflow when calculating with 'exponent'. + */ if (exponent >= 0 && exponent-1 > maxDigits-numSigDigs) { retval = HUGE_VAL; goto returnValue; |