GCC, the GNU Compiler Collection, recently fixed this eight and a half year old bug: “GCC Bugzilla – Bug 21718: real.c rounding not perfect.” This bug was the cause of incorrect decimal string to binary floating-point conversions. I first wrote about it over three years ago, and then recently in September and October. I also just wrote a detailed description of GCC’s conversion algorithm last month.
This fix, which will be available in version 4.9, scraps the old algorithm and replaces it with a call to MPFR function mpfr_strtofr(). I tested the fix on version 4.8.1, replacing its copy of gcc/real.c with the fixed one. I found no incorrect conversions.
Description of The Fix
GCC calls mpfr_strtofr() with a precision of SIGNIFICAND_BITS and a rounding mode of round-to-zero. GCC’s computation of SIGNIFICAND_BITS remains unchanged; it is 160 bits for 32-bit systems, and 192 bits for 64-bit systems. (I still have no explanation for why this constant is architecture-dependent. But it ends up not mattering anymore.) mpfr_strtofr() uses sufficient precision for its calculations internally, but then rounds its result to SIGNIFICAND_BITS. It also returns a value indicating whether the conversion was exact or not.
GCC uses its existing MPFR infrastructure to convert mpfr_strtofr()’s result to GCC’s real type. Interestingly, the process involves calling mpfr_get_str() to create a hexadecimal floating-point constant, and then calling real_from_string() — the same function that calls mpfr_strtofr(), but a different path — to convert that to the real type. GCC then rounds that real result to the target precision as before, using round-to-nearest/ties-to-even rounding. (Use of an intermediate rounded-to-zero result in combination with knowledge of an inexact conversion avoids double rounding errors.)
Here are a few things I wanted to note:
- The old conversion code still exists in real.c, presumably because it is used for other purposes.
- In the process of testing this fix, I found a bug in David Gay’s strtod().
- Since glibc strtod() has also recently been fixed, compile time and run time conversions will now match.