strtod: work around IRIX 6.5 bug
authorEric Blake <eblake@redhat.com>
Thu, 2 Sep 2010 03:34:44 +0000 (21:34 -0600)
committerEric Blake <eblake@redhat.com>
Wed, 8 Sep 2010 17:38:58 +0000 (11:38 -0600)
IRIX mis-parses "1e 1" as 10.0 and "" instead of 1.0 and "e 1".
Because the original parse may differ from the reparse in terms
of whether the value overflows, we have to do an errno dance.

* lib/strtod.c (strtod): Reparse number on shorter string if
exponent parse was invalid.
* tests/test-strtod.c (main): Add check for "0x1p 2".
Reported by Tom G. Christensen.

Signed-off-by: Eric Blake <eblake@redhat.com>
ChangeLog
lib/strtod.c
tests/test-strtod.c

index d880808cca194a6855419f905047e3056e9d32a3..9fb8803ce42d8b34424f4cca5b82e9fcf7aeb190 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,11 @@
 2010-09-08  Eric Blake  <eblake@redhat.com>
 
+       strtod: work around IRIX 6.5 bug
+       * lib/strtod.c (strtod): Reparse number on shorter string if
+       exponent parse was invalid.
+       * tests/test-strtod.c (main): Add check for "0x1p 2".
+       Reported by Tom G. Christensen.
+
        getopt: optimize previous patch
        * m4/getopt.m4 (gl_GETOPT_CHECK_HEADERS): Correctly check for
        empty variable.  Speed up awk script.
index 64b62ffd21c180a7fb8f79a10fd7a9eba60f9cd8..060dffa20979bf6e75e718af626b13ddc72af41e 100644 (file)
@@ -24,6 +24,7 @@
 #include <limits.h>
 #include <math.h>
 #include <stdbool.h>
+#include <string.h>
 
 #include "c-ctype.h"
 
@@ -202,6 +203,7 @@ strtod (const char *nptr, char **endptr)
   const char *s = nptr;
   const char *end;
   char *endbuf;
+  int saved_errno;
 
   /* Eat whitespace.  */
   while (locale_isspace (*s))
@@ -212,6 +214,7 @@ strtod (const char *nptr, char **endptr)
   if (*s == '-' || *s == '+')
     ++s;
 
+  saved_errno = errno;
   num = underlying_strtod (s, &endbuf);
   end = endbuf;
 
@@ -239,6 +242,35 @@ strtod (const char *nptr, char **endptr)
                 end = p;
             }
         }
+      else
+        {
+          /* If "1e 1" was misparsed as 10.0 instead of 1.0, re-do the
+             underlying strtod on a copy of the original string
+             truncated to avoid the bug.  */
+          const char *e = s + 1;
+          while (e < end && c_tolower (*e) != 'e')
+            e++;
+          if (e < end && ! c_isdigit (e[1 + (e[1] == '-' || e[1] == '+')]))
+            {
+              char *dup = strdup (s);
+              errno = saved_errno;
+              if (!dup)
+                {
+                  /* Not really our day, is it.  Rounding errors are
+                     better than outright failure.  */
+                  num = parse_number (s, 10, 10, 1, 'e', &endbuf);
+                }
+              else
+                {
+                  dup[e - s] = '\0';
+                  num = underlying_strtod (dup, &endbuf);
+                  saved_errno = errno;
+                  free (dup);
+                  errno = saved_errno;
+                }
+              end = e;
+            }
+        }
 
       s = end;
     }
index 694e15d9667d15703db5ed3534a5328cda80c6ec..e1d04af3b9f54085afe6b1d9ad6b02e485f4044b 100644 (file)
@@ -558,6 +558,26 @@ main (void)
     ASSERT (ptr == input + 10);
     ASSERT (errno == ERANGE);
   }
+  {
+    const char input[] = "1E 1000000";
+    char *ptr;
+    double result;
+    errno = 0;
+    result = strtod (input, &ptr);
+    ASSERT (result == 1.0);             /* HP-UX 11.11, IRIX 6.5, OSF/1 4.0 */
+    ASSERT (ptr == input + 1);          /* HP-UX 11.11, IRIX 6.5 */
+    ASSERT (errno == 0);
+  }
+  {
+    const char input[] = "0x1P 1000000";
+    char *ptr;
+    double result;
+    errno = 0;
+    result = strtod (input, &ptr);
+    ASSERT (result == 1.0);             /* NetBSD 3.0, OpenBSD 4.0, AIX 7.1, HP-UX 11.11, IRIX 6.5, OSF/1 5.1, Solaris 10, mingw */
+    ASSERT (ptr == input + 3);          /* NetBSD 3.0, OpenBSD 4.0, AIX 7.1, HP-UX 11.11, IRIX 6.5, OSF/1 5.1, Solaris 10, mingw */
+    ASSERT (errno == 0);
+  }
 
   /* Infinity.  */
   {
@@ -831,6 +851,16 @@ main (void)
     ASSERT (ptr == input + 6);          /* NetBSD 3.0, OpenBSD 4.0, AIX 5.1, HP-UX 11.11, IRIX 6.5, OSF/1 5.1, Solaris 10, mingw */
     ASSERT (errno == 0);
   }
+  {
+    const char input[] = "0x1p 2";
+    char *ptr;
+    double result;
+    errno = 0;
+    result = strtod (input, &ptr);
+    ASSERT (result == 1.0);             /* NetBSD 3.0, OpenBSD 4.0, AIX 7.1, HP-UX 11.11, IRIX 6.5, OSF/1 5.1, Solaris 10, mingw */
+    ASSERT (ptr == input + 3);          /* NetBSD 3.0, OpenBSD 4.0, AIX 7.1, HP-UX 11.11, IRIX 6.5, OSF/1 5.1, Solaris 10, mingw */
+    ASSERT (errno == 0);
+  }
 
   /* Large buffers.  */
   {