public inbox for glibc-bugs@sourceware.org
help / color / mirror / Atom feed
From: "john at thesalmons dot org" <sourceware-bugzilla@sourceware.org>
To: glibc-bugs@sources.redhat.com
Subject: [Bug libc/3479] Incorrect rounding in strtod()
Date: Sun, 04 Feb 2007 19:37:00 -0000	[thread overview]
Message-ID: <20070204193713.5139.qmail@sourceware.org> (raw)
In-Reply-To: <20061107172025.3479.hack@watson.ibm.com>


------- Additional Comments From john at thesalmons dot org  2007-02-04 19:37 -------
I just encountered something very similar.  I started looking at C++
operator>>, which led me to scanf, which led me to strtof.  It appears
that glibc's strtof doesn't adhere to the "Recommended Practice" of
section 6.4.4.2 and 7.20.1.3 of C99.  I see numerous FAILs in the
attached code with strtof.  I haven't seen any problems with strtod,
but I haven't done exhaustive tests.  Platforms:

  Opteron, Centos, glibc-2.3.4-2, gcc-4.1.1
  Pentium 4, Fedora, glibc-2.3.3-27.1, gcc-3.3.3-7

I have not tested against glibc's CVS, but even if it passes, I would
suggest that something like the tests here belong in the test suite.

The recommended practice for literal floating constants in 6.4.4.2 is:

     The translation-time conversion of floating constants should
     match the execution-time conversion of character strings by library
     functions, such as strtod, given matching inputs suitable for both
     conversions, the same result format, and default execution-time
     rounding.63) 

     63) The specification for the library functions
     recommends more accurate conversion than required for floating
     constants (see 7.20.1.3).

The attached code checks this condition.  It's *much* easier to check
this than to check the more detailed requirement of 7.20.1.3.  As a
result, the code doesn't actually prove that glibc is in error.  It's
possible that gcc's handling of literal floats is in error.  


The recommended practice for strto* in 7.20.1.3 is:

     If the subject sequence has the decimal form and at most
     DECIMAL_DIG (defined in <float.h>) significant digits, the result
     should be correctly rounded. If the subject sequence D has the
     decimal form and more than DECIMAL_DIG significant digits,
     consider the two bounding, adjacent decimal strings L and U, both
     having DECIMAL_DIG significant digits, such that the values of L,
     D, and U satisfy L <= D <= U. The result should be one of the
     (equal or adjacent) values that would be obtained by correctly
     rounding L and U according to the current rounding direction,
     with the extra stipulation that the error with respect to D
     should have a correct sign for the current rounding direction.

The decimal strings in the attached test case are all less than or
equal to DECIMAL_DIG in length.

A close look at the output suggests that glibc is in error, at least
if one believes the output of printf("%.30g").  For example, for the
first FAIL:

The input string is:
"1.00000005960464477550"
The possible neighboring floats are:
"1.00000011920928955078125000000"  (printf("%.30g", 1 + FLT_EPSILON))
and
"1.0"

According to dc, the distance from the input to 1+FLT_EPSILON is:
.00000005960464477528125000000
and the distance from the input to 1.0 is
.00000005960464477550

so 1+FLT_EPSILON should be the "correct" properly rounded result.
strtof returns 1.0.

---------------------------------------

[jsalmon@river lexical_cast]$ cat badstrtod2.c
#define _ISOC99_SOURCE_
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <math.h>
#include <float.h>

void checkf(const char *s, float f){
    float cf = strtof(s, NULL);
    long double cld = strtold(s, NULL);

    if( cf != f ){
	printf("FAIL strtof (\"%s\") %a is not the same as literal %a\n",
	       s, cf, f);
    }else{
	printf("OK   strtof (\"%s\") %a is the same as literal\n",
	       s, cf);
    }
}

void checkd(const char *s, double d){
    double cd = strtod(s, NULL);
    if( cd != d ){
	printf("FAIL strtod (\"%s\") %a is not the same as literal %a\n",
	       s, cd, d);
    }else{
	printf("OK   strtod (\"%s\") %a is the same as literal\n",
	       s, cd);
    }
}

int main(int argc, char **argv){
    long double ld;

    /* Put some context in the output.  These are not used for testing */
    ld = 1.L + (long double)FLT_EPSILON;
    printf("1 + FLT_EPSILON: %La %#.30Lg\n", ld, ld);
    ld = 1.L + ((long double)FLT_EPSILON)/2.;
    printf("1 + FLT_EPSILON/2.: %La %#.30Lg\n", ld, ld);
    ld = 1.L + ((long double)FLT_EPSILON)/2. + LDBL_EPSILON;
    printf("1 + FLT_EPSILON/2. + LDBL_EPSILON: %La %#.30Lg\n", ld, ld);

#define CHKF(s) checkf(#s, s##f);
    /* 1 + FLT_EPSILON/2. + LDBL_EPSILON */
    CHKF(1.00000005960464477550);
    CHKF(1.0000000596046447755);
    CHKF(1.000000059604644776);
    CHKF(1.000000059604644775);
    CHKF(1.00000005960464478);
    CHKF(1.0000000596046448);
    CHKF(1.000000059604645);
    CHKF(1.00000005960464);
    CHKF(1.0000000596046);
    CHKF(1.000000059605);
    CHKF(1.00000005960);
    CHKF(1.0000000596);
    CHKF(1.000000060);
    CHKF(1.00000006);
    CHKF(1.0000001);
    CHKF(1.000000);

    ld = 1.L + (long double)DBL_EPSILON;
    printf("1 + DBL_EPSILON: %La %#.30Lg\n", ld, ld);
    ld = 1.L + ((long double)DBL_EPSILON)/2.;
    printf("1 + DBL_EPSILON/2.: %La %#.30Lg\n", ld, ld);
    ld = 1.L + ((long double)DBL_EPSILON)/2. + LDBL_EPSILON;
    printf("1 + DBL_EPSILON/2. + LDBL_EPSILON: %La %#.30Lg\n", ld, ld);

#define CHKD(s) checkd(#s, s);
    CHKD(1.00000000000000011113);
    CHKD(1.00000000000000011103);
    CHKD(1.00000000000000011102);
    CHKD(1.00000000000000011101);
    CHKD(1.0000000000000001111);
    CHKD(1.000000000000000111);
    CHKD(1.00000000000000011);
    CHKD(1.0000000000000001);

    return 0;
}
[jsalmon@river lexical_cast]$ 
[jsalmon@river lexical_cast]$ gcc -g -std=c99  badstrtod2.c -lm -o badstrtod2 &&
badstrtod2 
1 + FLT_EPSILON: 0x8.00001p-3 1.00000011920928955078125000000
1 + FLT_EPSILON/2.: 0x8.000008p-3 1.00000005960464477539062500000
1 + FLT_EPSILON/2. + LDBL_EPSILON: 0x8.000008000000001p-3
1.00000005960464477549904521725
FAIL strtof ("1.00000005960464477550") 0x1p+0 is not the same as literal
0x1.000002p+0
FAIL strtof ("1.0000000596046447755") 0x1p+0 is not the same as literal
0x1.000002p+0
FAIL strtof ("1.000000059604644776") 0x1p+0 is not the same as literal 0x1.000002p+0
OK   strtof ("1.000000059604644775") 0x1p+0 is the same as literal
FAIL strtof ("1.00000005960464478") 0x1p+0 is not the same as literal 0x1.000002p+0
FAIL strtof ("1.0000000596046448") 0x1p+0 is not the same as literal 0x1.000002p+0
FAIL strtof ("1.000000059604645") 0x1p+0 is not the same as literal 0x1.000002p+0
OK   strtof ("1.00000005960464") 0x1p+0 is the same as literal
OK   strtof ("1.0000000596046") 0x1p+0 is the same as literal
FAIL strtof ("1.000000059605") 0x1p+0 is not the same as literal 0x1.000002p+0
OK   strtof ("1.00000005960") 0x1p+0 is the same as literal
OK   strtof ("1.0000000596") 0x1p+0 is the same as literal
OK   strtof ("1.000000060") 0x1.000002p+0 is the same as literal
OK   strtof ("1.00000006") 0x1.000002p+0 is the same as literal
OK   strtof ("1.0000001") 0x1.000002p+0 is the same as literal
OK   strtof ("1.000000") 0x1p+0 is the same as literal
1 + DBL_EPSILON: 0x8.0000000000008p-3 1.00000000000000022204460492503
1 + DBL_EPSILON/2.: 0x8.0000000000004p-3 1.00000000000000011102230246252
1 + DBL_EPSILON/2. + LDBL_EPSILON: 0x8.000000000000401p-3
1.00000000000000011113072267976
OK   strtod ("1.00000000000000011113") 0x1.0000000000001p+0 is the same as literal
OK   strtod ("1.00000000000000011103") 0x1.0000000000001p+0 is the same as literal
OK   strtod ("1.00000000000000011102") 0x1p+0 is the same as literal
OK   strtod ("1.00000000000000011101") 0x1p+0 is the same as literal
OK   strtod ("1.0000000000000001111") 0x1.0000000000001p+0 is the same as literal
OK   strtod ("1.000000000000000111") 0x1p+0 is the same as literal
OK   strtod ("1.00000000000000011") 0x1p+0 is the same as literal
OK   strtod ("1.0000000000000001") 0x1p+0 is the same as literal
[jsalmon@river lexical_cast]$ 


-- 


http://sourceware.org/bugzilla/show_bug.cgi?id=3479

------- You are receiving this mail because: -------
You are on the CC list for the bug, or are watching someone who is.


  reply	other threads:[~2007-02-04 19:37 UTC|newest]

Thread overview: 27+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2006-11-07 17:20 [Bug libc/3479] New: " hack at watson dot ibm dot com
2007-02-04 19:37 ` john at thesalmons dot org [this message]
2007-02-04 19:44 ` [Bug libc/3479] " john at thesalmons dot org
2007-02-05 16:48 ` joseph at codesourcery dot com
2007-02-05 18:32 ` hack at watson dot ibm dot com
2007-02-05 21:54 ` john at thesalmons dot org
2007-02-12 17:04 ` jakub at redhat dot com
2007-10-22 14:47 ` vincent+libc at vinc17 dot org
2009-07-15 14:59 ` Sylvain dot Pion at sophia dot inria dot fr
2009-07-15 15:23 ` Sylvain dot Pion at sophia dot inria dot fr
2009-07-15 15:50 ` vincent+libc at vinc17 dot org
2009-07-15 16:21 ` Sylvain dot Pion at sophia dot inria dot fr
2010-06-04  1:02 ` exploringbinary at gmail dot com
2010-06-04  1:03 ` exploringbinary at gmail dot com
2010-09-02 16:25 ` schwab at linux-m68k dot org
     [not found] <bug-3479-131@http.sourceware.org/bugzilla/>
2011-11-12 16:11 ` mwelinder at gmail dot com
2012-05-02 10:03 ` jsm28 at gcc dot gnu.org
2012-05-02 15:23 ` bugdal at aerifal dot cx
2012-05-02 16:01 ` floitsch at google dot com
2012-05-02 16:55 ` joseph at codesourcery dot com
2012-08-10 23:41 ` jsm28 at gcc dot gnu.org
2012-08-25 15:28 ` jsm28 at gcc dot gnu.org
2012-08-25 15:30 ` floitsch at google dot com
2012-08-27 16:13 ` jsm28 at gcc dot gnu.org
2013-11-12 16:43 ` exploringbinary at gmail dot com
2014-02-16 16:57 ` jackie.rosen at hushmail dot com
2014-05-28 19:44 ` schwab at sourceware dot org

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20070204193713.5139.qmail@sourceware.org \
    --to=sourceware-bugzilla@sourceware.org \
    --cc=glibc-bugs@sources.redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).