summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjan.nijtmans <nijtmans@users.sourceforge.net>2022-07-17 13:03:42 (GMT)
committerjan.nijtmans <nijtmans@users.sourceforge.net>2022-07-17 13:03:42 (GMT)
commitfb1fd5d59f34d2ac112093399fdd1487d8978981 (patch)
tree2869252d5d2186be9ab011f550d88ad7f0c97206
parent505c18d593b11fa682694a7653b17b325c1aac0e (diff)
parent9e8528863dde14a2236d2cc2b012254a77418753 (diff)
downloadtcl-fb1fd5d59f34d2ac112093399fdd1487d8978981.zip
tcl-fb1fd5d59f34d2ac112093399fdd1487d8978981.tar.gz
tcl-fb1fd5d59f34d2ac112093399fdd1487d8978981.tar.bz2
Fix [713653b951]: Floating point precision problems on x86 musl
-rw-r--r--generic/tclStrToD.c42
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;