Three years ago I wrote about how the gcc C compiler and the glibc strtod() function do some decimal to double-precision floating-point conversions incorrectly. I recently retested their conversions and found out two things: glibc’s strtod() has been fixed, and gcc’s conversion code, while still unfixed, produces correct conversions on some machines.
(Update 12/3/13: GCC now does correct conversions.)
(Update 11/12/13: While glibc strtod()’s updated conversion algorithm is fundamentally sound, it did convert one input incorrectly — but now that’s been fixed.)
(Update 10/9/13: I’ve since found some incorrect conversions in gcc.)
New Results From My Old Test Program
In my original article I included a test program that me and many of my readers ran. When I run it now, on a 64-bit Intel Core i5 running Ubuntu Linux 12.04.3 with gcc 4.6.3 and glibc 2.18, it shows that all the conversions are done correctly:
0.500000000000000166533453693773481063544750213623046875 Correct = 0x1.0000000000002p-1 gcc = 0x1.0000000000002p-1 strtod = 0x1.0000000000002p-1 3.518437208883201171875e13 Correct = 0x1.0000000000002p+45 gcc = 0x1.0000000000002p+45 strtod = 0x1.0000000000002p+45 62.5364939768271845828 Correct = 0x1.f44abd5aa7ca4p+5 gcc = 0x1.f44abd5aa7ca4p+5 strtod = 0x1.f44abd5aa7ca4p+5 8.10109172351e-10 Correct = 0x1.bd5cbaef0fd0cp-31 gcc = 0x1.bd5cbaef0fd0cp-31 strtod = 0x1.bd5cbaef0fd0cp-31 1.50000000000000011102230246251565404236316680908203125 Correct = 0x1.8p+0 gcc = 0x1.8p+0 strtod = 0x1.8p+0 9007199254740991.4999999999999999999999999999999995 Correct = 0x1.fffffffffffffp+52 gcc = 0x1.fffffffffffffp+52 strtod = 0x1.fffffffffffffp+52
GLIBC
The fix to glibc strtod() was done in version 2.17. I tested against glibc 2.18, which I installed on top of Ubuntu 12.04.3.
Besides the testcase above, I ran an automated testcase that generates random decimal strings and compares glibc strtod()’s results to the results from David Gay’s strtod(). I have found no incorrect conversions.
Unfixed glibc on Different Machines
I tried my testcase on a vanilla Ubuntu 12.04.3 system, which comes with eglibc 2.15 — an unfixed version. (eglibc is the same as glibc, as far as strtod() is concerned.) As expected, the testcase produced incorrect conversions in strtod(). Interestingly however, the output differed slightly from my original results, which were on a 32-bit machine; one of the incorrect ones, 8.10109172351e-10, is now correct:
0.500000000000000166533453693773481063544750213623046875
Correct = 0x1.0000000000002p-1
gcc = 0x1.0000000000002p-1
strtod = 0x1.0000000000001p-1
3.518437208883201171875e13
Correct = 0x1.0000000000002p+45
gcc = 0x1.0000000000002p+45
strtod = 0x1.0000000000001p+45
62.5364939768271845828
Correct = 0x1.f44abd5aa7ca4p+5
gcc = 0x1.f44abd5aa7ca4p+5
strtod = 0x1.f44abd5aa7ca3p+5
8.10109172351e-10
Correct = 0x1.bd5cbaef0fd0cp-31
gcc = 0x1.bd5cbaef0fd0cp-31
strtod = 0x1.bd5cbaef0fd0cp-31
1.50000000000000011102230246251565404236316680908203125
Correct = 0x1.8p+0
gcc = 0x1.8p+0
strtod = 0x1.8p+0
9007199254740991.4999999999999999999999999999999995
Correct = 0x1.fffffffffffffp+52
gcc = 0x1.fffffffffffffp+52
strtod = 0x1.fffffffffffffp+52
I think this is an architecture related issue. An unfixed glibc on 32-bit machines produces the incorrect conversion, but an unfixed glibc on 64-bit machines produces a correct conversion. This view is supported by the results of Vincent Lefèvre and others.
GCC
gcc has not been fixed, but I cannot find any incorrect conversions now. Besides the testcase above, I wrote an automated testcase that generates, compiles, and executes C programs on the fly. The program compares the conversion of a decimal literal by gcc against the conversion of that same decimal value as a string passed to David Gay’s strtod().
The best explanation is that this is an architecture-dependent bug. This view is supported by the results of Vincent Lefèvre and others. Is it simply that gcc is incorrect on 32-bit and correct on 64-bit? I don’t know. Looking back at my readers’ output, I see one who reported 32-bit-like incorrect conversions on a 64-bit PowerPC.
Please Run This Testcase
Please run my testcase and report your results. Let me know what versions of glibc and gcc you are using, and what system you are on. I am especially interested in how a fixed version of glibc runs on 32-bit machines, and how any version of gcc works on non-Intel 64-bit machines.
Gentoo/Linux amd64 AMD FX(tm)-6350 Six-Core Processor
sys-libs/glibc-2.20-r2
gcc (Gentoo 4.5.4 p1.2, pie-0.4.7) 4.5.4 all 6 testcases ok
gcc (Gentoo 4.7.4 p1.3, pie-0.5.5) 4.7.4 all 6 testcases ok
gcc (Gentoo 4.8.5 p1.3, pie-0.6.2) 4.8.5 all 6 testcases ok
gcc (Gentoo 5.3.0 p1.0, pie-0.6.5) 5.3.0 all 6 testcases ok
gcc (Gentoo 4.9.3 p1.3, pie-0.6.3) 4.9.3 all 6 testcases ok
“all 6 testcases ok” means this output for normal gcc run and gcc -m32:
0.500000000000000166533453693773481063544750213623046875
Correct = 0x1.0000000000002p-1
gcc = 0x1.0000000000002p-1
strtod = 0x1.0000000000002p-1
3.518437208883201171875e13
Correct = 0x1.0000000000002p+45
gcc = 0x1.0000000000002p+45
strtod = 0x1.0000000000002p+45
62.5364939768271845828
Correct = 0x1.f44abd5aa7ca4p+5
gcc = 0x1.f44abd5aa7ca4p+5
strtod = 0x1.f44abd5aa7ca4p+5
8.10109172351e-10
Correct = 0x1.bd5cbaef0fd0cp-31
gcc = 0x1.bd5cbaef0fd0cp-31
strtod = 0x1.bd5cbaef0fd0cp-31
1.50000000000000011102230246251565404236316680908203125
Correct = 0x1.8p+0
gcc = 0x1.8p+0
strtod = 0x1.8p+0
9007199254740991.4999999999999999999999999999999995
Correct = 0x1.fffffffffffffp+52
gcc = 0x1.fffffffffffffp+52
strtod = 0x1.fffffffffffffp+52
@bothie,
Thanks for your report. Both GCC and glibc have been working for about three years.
Ubuntu 16.04.2 / Linux x86_64 Intel Core i5-2400
0.500000000000000166533453693773481063544750213623046875
Correct = 0x1.0000000000002p-1
gcc = 0x1.0000000000002p-1
strtod = 0x1.0000000000002p-1
3.518437208883201171875e13
Correct = 0x1.0000000000002p+45
gcc = 0x1.0000000000002p+45
strtod = 0x1.0000000000002p+45
62.5364939768271845828
Correct = 0x1.f44abd5aa7ca4p+5
gcc = 0x1.f44abd5aa7ca4p+5
strtod = 0x1.f44abd5aa7ca4p+5
8.10109172351e-10
Correct = 0x1.bd5cbaef0fd0cp-31
gcc = 0x1.bd5cbaef0fd0cp-31
strtod = 0x1.bd5cbaef0fd0cp-31
1.50000000000000011102230246251565404236316680908203125
Correct = 0x1.8p+0
gcc = 0x1.8p+0
strtod = 0x1.8p+0
9007199254740991.4999999999999999999999999999999995
Correct = 0x1.fffffffffffffp+52
gcc = 0x1.fffffffffffffp+52
strtod = 0x1.fffffffffffffp+52