delorie.com/archives/browse.cgi   search  
Mail Archives: djgpp-workers/2003/03/22/06:35:14

From: <ams AT ludd DOT luth DOT se>
Message-Id: <200303221132.h2MBWDc22095@speedy.ludd.luth.se>
Subject: strtof()'s NaN and Inf support
To: DJGPP-WORKERS <djgpp-workers AT delorie DOT com>
Date: Sat, 22 Mar 2003 12:32:13 +0100 (CET)
X-Mailer: ELM [version 2.4ME+ PL78 (25)]
MIME-Version: 1.0
X-MailScanner: Found to be clean
Reply-To: djgpp-workers AT delorie DOT com

Hello.

Here's a patch for NaN and Inf support in strtof(). Read the part
documentation part carefully. The text about SNaN -> QNaN is based on
the following results from running t-strtof:

strtof("nan(0x401234)", &endptr) -> NaN, 13 - OK
strtof("-nan(0x400088)", &endptr) -> NaN, 14 - OK
strtof("nan(0x1234)", &endptr) -> NaN, 11 - mantissa == 0x401234 != 0x1234 - FAIL
strtof("-nan(0x88)", &endptr) -> NaN, 10 - mantissa == 0x400088 != 0x88 - FAIL

I'll change the test case so it won't check this particular "failure"
after we are content with the wording. (This failure won't happen if
the FPU is emulated which will be discussed in another mail because
there are other failures then.)


Right,

						MartinS

Index: djgpp/src/libc/c99/stdlib/strtof.c
===================================================================
RCS file: /cvs/djgpp/djgpp/src/libc/c99/stdlib/strtof.c,v
retrieving revision 1.2
diff -p -u -r1.2 strtof.c
--- djgpp/src/libc/c99/stdlib/strtof.c  23 Jan 2003 19:53:02 -0000      1.2
+++ djgpp/src/libc/c99/stdlib/strtof.c  22 Mar 2003 11:13:56 -0000
@@ -5,12 +5,15 @@
 /* Copyright (C) 1997 DJ Delorie, see COPYING.DJ for details */
 /* Copyright (C) 1996 DJ Delorie, see COPYING.DJ for details */
 /* Copyright (C) 1994 DJ Delorie, see COPYING.DJ for details */
+#include <libc/stubs.h>
 #include <math.h>
 #include <stdlib.h>
 #include <float.h>
 #include <errno.h>
 #include <ctype.h>
+#include <string.h>
 #include <libc/unconst.h>
+#include <libc/ieee.h>
 
 float
 strtof(const char *s, char **sret)
@@ -43,6 +46,73 @@ strtof(const char *s, char **sret)
     s++;
   }
 
+  /* Handle INF and INFINITY. */
+  if ( ! strnicmp( "INF", s, 3 ) )
+  {
+    if( sret )
+    {
+      if ( ! strnicmp( "INITY", &s[3], 5 ) )
+      {
+       *sret = unconst((&s[8]), char *);
+      }
+      else
+      {
+       *sret = unconst((&s[3]), char *);
+      }
+    }
+
+    if( 0 <= sign )
+    {
+      return INFINITY;
+    }
+    else
+    {
+      return -INFINITY;
+    }
+  }
+
+  /* Handle NAN and NAN(<hex-number>). */
+  if ( ! strnicmp( "NAN", s, 3 ) )
+  {
+    float f = NAN;
+    float_t n = *(float_t *)(&f);
+
+    if ( sign < 0 )
+    {
+      n.sign = 1;
+    }
+
+    if ( s[3] == '(' )
+    {
+      long mantissa_bits = 0;
+      char *endptr = unconst((&s[4]), char *);
+
+      mantissa_bits = strtol(&s[4], &endptr, 16);
+      if ( *endptr == ')' )
+      {
+       if ( mantissa_bits )
+       {
+           n.mantissa = mantissa_bits & 0x7fffff;
+       }
+       if ( sret )
+       {
+         *sret = endptr+1;
+       }
+       return *(float *)(&n);
+      }
+
+      /* The subject sequence didn't match NAN(<hex-number>), so match
+        only NAN. */
+    }
+
+    if( sret )
+    {
+      *sret = unconst((&s[3]), char *);
+    }
+    return *(float *)(&n);
+  }
+
+  /* Handle ordinary numbers. */
   while ((*s >= '0') && (*s <= '9'))
   {
     flags |= 1;
Index: djgpp/src/libc/c99/stdlib/strtof.txh
===================================================================
RCS file: /cvs/djgpp/djgpp/src/libc/c99/stdlib/strtof.txh,v
retrieving revision 1.4
diff -p -u -r1.4 strtof.txh
--- djgpp/src/libc/c99/stdlib/strtof.txh        29 Jan 2003 12:34:24 -0000      1.4
+++ djgpp/src/libc/c99/stdlib/strtof.txh        22 Mar 2003 11:13:56 -0000
@@ -11,7 +11,9 @@ float strtof(const char *s, char **endp)
 @subheading Description
 
 This function converts as many characters of @var{s} as look like a
-floating point number into that number.  If @var{endp} is not a null
+floating point number into that number.  It also recognises
+(case-insensitivly) @code{Inf}, @code{Infinity}, @code{NaN} and
+@code{NaN(@var{optional hex-number}}.  If @var{endp} is not a null
 pointer, @code{*endp} is set to point to the first unconverted
 character.
 
@@ -19,12 +21,37 @@ character.
 
 The value the represented by @var{s}.
 
+If @var{s} is ``Inf'' or ``Infinity'', with any variations of case and
+optionally prefixed with ``+'' or ``-'', the return value is
+@code{INFINITY} (if no prefix or a ``+'' prefix) or @code{-INFINITY}
+(if the prefix is ``-'').
+
+If @var{s} is ``NaN'' or ``NaN()'', with any variations of case and
+optionally prefixed with ``+'' or ``-'', the return value is
+@code{NAN}.  If the prefix is ``-'' the sign bit in the NaN will be
+set to 1.
+
+If @var{s} is ``NaN(@var{hex-number})'', with any variations of case
+and optionally prefixed with ``+'' or ``-'', the return value is a NaN
+with the mantissa bits set to @code{@var{hex-number}&0x7fffff} (the
+mantissa for floats consists of 23 bits).  Use at most 8 hexadecimal
+digits in @var{hex-number} or the internal conversion will overflow,
+which results in a mantissa of 7fffff.  If
+@code{@var{hex-number}&0x7fffff} is 0 (which won't work as a
+representation of a NaN) @code{NAN} will be returned.  If the prefix
+is ``-'' the sign bit in the NaN will be set to 1.  Testing shows that
+SNaNs might be converted into QNaNs (most significant bit will be set
+in the mantissa).
+
 If a number represented by @var{s} doesn't fit into the range of values
 representable by the type @code{float}, the function returns either
 @code{-HUGE_VALF} (if @var{s} begins with the character @code{-}) or
 @code{+HUGE_VALF}, and sets @code{errno} to @code{ERANGE}.
 
 @subheading Portability
+
+@port-note ansi-c99 Support for ``Inf'', ``Infinity'', ``NaN'' and
+``NaN(@dots{})'' was standardised in ANSI C99.
 
 @portability !ansi-c89, ansi-c99, !posix-1003.2-1992, posix-1003.1-2001
 
Index: djgpp/tests/libc/c99/stdlib/t-strtof.c
===================================================================
RCS file: /cvs/djgpp/djgpp/tests/libc/c99/stdlib/t-strtof.c,v
retrieving revision 1.2
diff -p -u -r1.2 t-strtof.c
--- djgpp/tests/libc/c99/stdlib/t-strtof.c      23 Jan 2003 19:53:57 -0000      1.2
+++ djgpp/tests/libc/c99/stdlib/t-strtof.c      22 Mar 2003 11:14:03 -0000
@@ -51,6 +51,68 @@ static const test3_t tests3[] = {
 
 static const size_t n_tests3 = sizeof(tests3) / sizeof(tests3[0]);
 
+typedef struct {
+  const char * const str; /* String to run strtof() on. */
+  const int diff; /* For endptr tests. How many characters from string start
+                    endptr should be offset. */
+} test4_t;
+
+static const test4_t tests4[] = {
+  { "inF",                 3 },
+  { "-INf",                4 },
+  { "infi",                3 },
+  { "-infi",               4 },
+  { "infinit",             3 },
+  { "-infinit",            4 },
+  { "INfINITY",            8 },
+  { "-InfInIty",           9 },
+  { "infinity0",           8 },
+  { "-infinity5",          9 },
+  { "infinity-1",          8 },
+  { "-infinity-6",         9 },
+};
+
+static const size_t n_tests4 = sizeof(tests4) / sizeof(tests4[0]);
+
+typedef struct {
+  const char * const str; /* String to run strtof() on. */
+  const int diff; /* For endptr tests. How many characters from string start
+                    endptr should be offset. */
+  const int sign; /* Sign bit. */
+  const int mantissa; /* What mantissa should be set to after
+                        conversion. 0 -> don't care as long as it's
+                        non-zero. */
+} test5_t;
+
+static const test5_t tests5[] = {
+  { "nAn",                         3,  0, 0 },
+  { "-nAn",                        4,  1, 0 },
+  { "nanny",                       3,  0, 0 },
+  { "-nanny",                      4,  1, 0 },
+  { "NAN()",                       5,  0, 0 },
+  { "NAN( )",                      3,  0, 0 },
+  { "-Nan()",                      6,  1, 0 },
+  { "nAN(0)",                      6,  0, 0 },
+  { "-nan(0)",                     7,  1, 0 },
+  { "nan(0x401234)",              13,  0, 0x401234 }, /* QNaN */
+  { "-nan(0x400088)",             14,  1, 0x400088 }, /* QNaN */
+  { "nan(0x1234)",                11,  0, 0x1234 }, /* Don't check: SNaN -> QNaN. */
+  { "-nan(0x88)",                 10,  1, 0x88 }, /* Don't check: SNaN -> QNaN. */
+  { "-nan(0xaa7d7aa74)",          17,  1, 0 }, /* Don't check: overflow. */
+  { "nan(0x12345678123456781)",   24,  0, 0 }, /* Don't check: overflow. */
+  { "-nan(0x12345678123456781)",  25,  1, 0 }, /* Don't check: overflow. */
+  { "naN(something)",              3,  0, 0 },
+  { "-nAn(smurf)",                 4,  1, 0 },
+  { "-nan(-nan)",                  4,  1, 0 },
+  { "nan(nan(nan()))",             3,  0, 0 },
+  { "NaN(0x1234oops)",             3,  0, 0 },
+  { "nan()()",                     5,  0, 0 },
+  { "NAN()nan",                    5,  0, 0 },
+};
+
+static const size_t n_tests5 = sizeof(tests5) / sizeof(tests5[0]);
+
+
 static void inline
 result (const size_t n, const float f_in, const float f_out)
 {
@@ -106,6 +168,60 @@ main (void)
        puts("No - OK");
     }
   }
-
+  
+  puts("Infinity tests:");
+  for (i = 0; i < n_tests4; i++) {
+    char *endptr;
+    float_t float_bits;
+
+    f_res = strtof(tests4[i].str, &endptr);
+      
+    printf("strtof(\"%s\", &endptr) -> %f, %ld - ", tests4[i].str,
+          f_res, endptr-tests4[i].str);
+
+    /* Need to do the Inf detection ourselves. */
+    float_bits = *(float_t *)(&f_res);
+    if (float_bits.exponent != 0xff || float_bits.mantissa != 0) {
+      puts("exponent != 0xff or mantissa == 0 - FAIL");
+    } else if ( (float_bits.sign && 0 < f_res ) || 
+               (!float_bits.sign && f_res < 0) ) {
+      puts("Wrong sign - FAIL");
+    } else if ( endptr-tests4[i].str != tests4[i].diff) {
+      printf("endptr-(start_of_string) == %ld != %d - FAIL\n",
+            endptr-tests4[i].str, tests4[i].diff);
+    } else {
+      puts("OK");
+    }
+  }
+  
+  puts("Nan tests:");
+  for (i = 0; i < n_tests5; i++) {
+    char *endptr;
+    float_t float_bits;
+
+    f_res = strtof(tests5[i].str, &endptr);
+
+    printf("strtof(\"%s\", &endptr) -> %f, %ld - ", tests5[i].str,
+          f_res, endptr-tests5[i].str);
+
+    /* Need to to the NaN detection ourselves. */
+    float_bits = *(float_t *)(&f_res);
+    if (float_bits.exponent != 0xff || float_bits.mantissa == 0) {
+      puts("exponent != 0xff or mantissa == 0 - FAIL");
+    } else if (tests5[i].mantissa && 
+              tests5[i].mantissa != float_bits.mantissa) { 
+      printf("mantissa == 0x%x != 0x%x - FAIL\n",
+            float_bits.mantissa, tests5[i].mantissa);
+    } else if (tests5[i].sign != float_bits.sign) {
+      printf("sign == %d != %d - FAIL\n", float_bits.sign,
+            tests5[i].sign);
+    } else if ( endptr-tests5[i].str != tests5[i].diff) {
+      printf("endptr-(start_of_string) == %ld != %d - FAIL\n",
+            endptr-tests5[i].str, tests5[i].diff);
+    } else {
+      puts("OK");
+    }
+  }
+  
   return(EXIT_SUCCESS);
 }

- Raw text -


  webmaster     delorie software   privacy  
  Copyright © 2019   by DJ Delorie     Updated Jul 2019