Menu

#2273 atof("inf") not standard-compliant (returns 0)

WSL
closed
Feature
fixed
IINR_-_Include_In_Next_Release
False
2016-11-29
2015-10-10
No

using:
MinGW gcc.exe (i686-posix-dwarf-rev1, Built by MinGW-W64 project) 4.9.2
MSYS2 in Windows 7

atof is not C standard compliant for "-inf", "inf", "-NaN", "NaN", "+NaN" (including all ignoring cases variations) as described at ISO/IEC 9899:1999, sections 7.20.1.1 & 7.20.1.3. It returns 0., but it should return infinity and quiet-NaN values for which there are valid representations under IEEE 754, as it does in Linux.

minimal case(s):

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main()
{
    char s[256];

    strcpy(s, "-inf");
    printf("atof(\"%s\") = %.17g\n", s, atof(s));

    strcpy(s, "inf");
    printf("atof(\"%s\") = %.17g\n", s, atof(s));

    strcpy(s, "-NaN");
    printf("atof(\"%s\") = %.17g\n", s, atof(s));

    strcpy(s, "nan");
    printf("atof(\"%s\") = %.17g\n", s, atof(s));

    strcpy(s, "+NAN");
    printf("atof(\"%s\") = %.17g\n", s, atof(s));

    return 1;
}

gives output:

$ ./a.exe
atof("-inf") = 0
atof("inf") = 0
atof("-NaN") = 0
atof("nan") = 0
atof("+NAN") = 0

Please make it standard compliant, or if there is a strong reason why this is already not done (maybe even intentionally implemented as it is now), please introduce compiler flag to enforce it.

Thanks!

Discussion

  • Keith Marshall

    Keith Marshall - 2015-10-11
    • Description has changed:

    Diff:

    --- old
    +++ new
    @@ -5,7 +5,7 @@
     atof is not C standard compliant for "-inf", "inf", "-NaN", "NaN", "+NaN" (including all ignoring cases variations) as described at ISO/IEC 9899:1999, sections 7.20.1.1 & 7.20.1.3. It returns 0., but it should return infinity and quiet-NaN values for which there are valid representations under IEEE 754, as it does in Linux.
    
     minimal case(s):
    -~~~
    +~~~~
     #include <stdio.h>
     #include <stdlib.h>
     #include <string.h>
    @@ -31,17 +31,17 @@
    
         return 1;
     }
    -~~~
    +~~~~
    
     gives output:
    -~~~
    +~~~~
     $ ./a.exe
     atof("-inf") = 0
     atof("inf") = 0
     atof("-NaN") = 0
     atof("nan") = 0
     atof("+NAN") = 0
    -~~~
    +~~~~
    
     Please make it standard compliant, or if there is a strong reason why this is already not done (maybe even intentionally implemented as it is now), please introduce compiler flag to enforce it.
    
    • status: unread --> assigned
    • assigned_to: Keith Marshall
    • Type: Bug --> Feature
     
  • Keith Marshall

    Keith Marshall - 2015-10-11

    Thanks for bringing this to our attention. We would point out that this is the bug tracker for products published by MinGW.org; these include neither the mingw-w64 GCC, nor the MSYS2 tools, which you are using; thus, while we will investigate the extent to which this issue affects our own products, we cannot help with products of unrelated projects.

    Do please note that our implementation of GCC uses the atof() function from Microsoft's MSVCRT.DLL runtime library, and I believe mingw-w64 does likewise. The behaviour you are reporting is a feature of that Microsoft atof() implementation, so perhaps you should ask them to "fix" it.

     
  • Keith Marshall

    Keith Marshall - 2015-10-11
    • Description has changed:

    Diff:

    --- old
    +++ new
    @@ -5,6 +5,7 @@
     atof is not C standard compliant for "-inf", "inf", "-NaN", "NaN", "+NaN" (including all ignoring cases variations) as described at ISO/IEC 9899:1999, sections 7.20.1.1 & 7.20.1.3. It returns 0., but it should return infinity and quiet-NaN values for which there are valid representations under IEEE 754, as it does in Linux.
    
     minimal case(s):
    +
     ~~~~
     #include <stdio.h>
     #include <stdlib.h>
    @@ -34,6 +35,7 @@
     ~~~~
    
     gives output:
    +
     ~~~~
     $ ./a.exe
     atof("-inf") = 0
    
     
  • Keith Marshall

    Keith Marshall - 2015-10-11

    FWIW, I can work around this, (with our <stdlib.h> implementation), by adding #define atof(S) (double)(strtold((S),NULL)) to my source file, after including the headers, and compiling with the -posix option; (without -posix, I get Microsoft's -1.#INF, 1.#INF, -1.#IND, -1.#IND, and -1.#IND representations of the results, which is still non-standard, but likely better than zeros).

    This work around suggests a possible simple resolution, for our products, to be incorporated into <stdlib.h> within a __USE_MINGW_ANSI_STDIO (internal) conditional compilation guard.

     
  • Hrvoje Abraham

    Hrvoje Abraham - 2015-10-11

    Thank you for your comments and attention. Your workaround is really elegant!

     
  • Hrvoje Abraham

    Hrvoje Abraham - 2015-10-11

    Why (double) cast for strtold(...), why not just strtod(...)?

     
    • Keith Marshall

      Keith Marshall - 2015-10-11

      strtod() is still Microsoft's implementation, and similarly ignores ISO-C standards. OTOH, strtold() is a MinGW.org extension, which aims for ISO-C conformity. You need the standards conforming behaviour of our strtold() implementation, and you then need to cast that to whatever type your application requires.

      BTW, do be aware that GCC's long double is a full 80-bit precision type, whereas in Microsoft's (non-)implementation, it is no more than an alias for their 64-bit double type. If you use long doubles, you need to be aware of this difference, particularly w.r.t. I/O; Microsoft's I/O routines cannot process 80-bit long doubles; MinGW.org offers an extended printf() implementation, (activated by compiling with -posix, -ansi, or any of the -std=c... options to GCC, or by defining _GNU_SOURCE, _BSD_SOURCE, _POSIX_SOURCE, _XOPEN_SOURCE=..., or _POSIX_C_SOURCE=... in your sources, before including any system header), which does interpret them correctly.

       
  • Hrvoje Abraham

    Hrvoje Abraham - 2015-10-11

    I get correct result with strtod(...), looks like only atof(...) is the problem:

    w/o -posix

    strtod("-inf") = -1.#INF
    strtod("nan") = 1.#QNAN
    

    -posix

    strtod("-inf") = -inf
    strtod("nan") = nan
    
     
    • Keith Marshall

      Keith Marshall - 2015-10-11

      But you are not using our tools! I'm only interested in a solution that works for us; whatever other projects may do is irrelevant.

      Consider the following variation on your original test case:

      $ cat foo.c
      #include <stdio.h>
      #include <stdlib.h>
      
      #define atof(S)  (strtod((S),NULL))
      
      static __inline__ int test( const char *s )
      { return printf("atof(\"%s\") = %.17g\n", s, atof(s)); }
      
      int main()
      {
        test("-inf");
        test("INF");
        test("-NaN");
        test("nan");
        test("+NAN");
        return 0;
      }
      

      Compile and run it on Win7 Home Edition, with or without -posix, and I see:

      $ gcc -Wall -Wextra -pedantic foo.c
      $ ./a.exe
      atof("-inf") = 0
      atof("INF") = 0
      atof("-NaN") = 0
      atof("nan") = 0
      atof("+NAN") = 0
      
      $ gcc -posix -Wall -Wextra -pedantic foo.c
      $ ./a.exe
      atof("-inf") = 0
      atof("INF") = 0
      atof("-NaN") = 0
      atof("nan") = 0
      atof("+NAN") = 0
      

      Change it, to use strtold() rather than strtod():

      $ cat foo.c
      #include <stdio.h>
      #include <stdlib.h>
      
      #define atof(S)  (double)(strtold((S),NULL))
      
      static __inline__ int test( const char *s )
      { return printf("atof(\"%s\") = %.17g\n", s, atof(s)); }
      
      int main()
      {
        test("-inf");
        test("INF");
        test("-NaN");
        test("nan");
        test("+NAN");
        return 0;
      }
      

      and the results now become:

      $ gcc -Wall -Wextra -pedantic foo.c
      $ ./a.exe
      atof("-inf") = -1.#INF
      atof("INF") = 1.#INF
      atof("-NaN") = -1.#IND
      atof("nan") = -1.#IND
      atof("+NAN") = -1.#IND
      
      $ gcc -posix -Wall -Wextra -pedantic foo.c
      $ ./a.exe
      atof("-inf") = -inf
      atof("INF") = inf
      atof("-NaN") = nan
      atof("nan") = nan
      atof("+NAN") = nan
      

      We use Microsoft's strtod() implementation, (from the platform MSVCRT.DLL), but our own strtold(). Maybe other projects also substitute their own strtod(), but that's irrelevant for us -- we need to use our strtold(), to ensure that we get standard conforming interpretation of infinities and NaNs.

       
  • Hrvoje Abraham

    Hrvoje Abraham - 2015-10-11

    Is there a list of MSVC (in)dependent MinGW (your project) methods available?

     
  • Keith Marshall

    Keith Marshall - 2015-10-23
    • status: assigned --> pending
    • Resolution: none --> fixed
    • Category: Unknown --> IINR_-_Include_In_Next_Release
     
  • Keith Marshall

    Keith Marshall - 2015-10-23

    There is an existing __strtod() implementation in libmingwex.a, which works correctly; (it also handles hexadecimal-float notation correctly, unlike Microsoft's strtod() implementation). A static __inline__ mapping of strtod() to __strtod() was removed from MinGW.org's <stdlib.h> in May-2011, (inappropriately, IMO), to circumvent an upstream bug in GCC's libquadmath, (which is where it should have been fixed).

    I've committed [987711], which reinstates the static __inline__ mapping of strtod() to __strtod(), and adds a corresponding mapping for atof(), but now guarded by __USE_MINGW_ANSI_STDIO; thus, explicitly defining any of the feature test macros which enables __USE_MINGW_ANSI_STDIO, (which you really should not define directly), or compiling with -posix, -ansi, or any of the -std=c... options, will allow your code to benefit from the remapped strtod() call.

    FWIW, I've compiled a cross-native build of gcc-4.9.3, (building on a GNU/Linux host), using the modified mingwrt; the libquadmath issue did not arise. I would be grateful if others will also test this modification.

     

    Related

    Commit: [987711]

  • Keith Marshall

    Keith Marshall - 2016-11-29
    • status: pending --> closed