Correctly Rounded Conversions in GCC and GLIBC

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.

Dingbat
RSS feed icon
RSS e-mail icon

3 comments

  1. 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

  2. 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

Leave a Reply

Your email address will not be published. Required fields are marked *

(Cookies must be enabled to leave a comment...it reduces spam.)