changeset 13655:d813f5516d9a

strtod: work around IRIX 6.5 bug 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>
author Eric Blake <eblake@redhat.com>
date Wed, 01 Sep 2010 21:34:44 -0600
parents 80cff54a83b0
children bc9f00d2bbf1
files ChangeLog lib/strtod.c tests/test-strtod.c
diffstat 3 files changed, 68 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- 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.
--- a/lib/strtod.c
+++ b/lib/strtod.c
@@ -24,6 +24,7 @@
 #include <limits.h>
 #include <math.h>
 #include <stdbool.h>
+#include <string.h>
 
 #include "c-ctype.h"
 
@@ -202,6 +203,7 @@
   const char *s = nptr;
   const char *end;
   char *endbuf;
+  int saved_errno;
 
   /* Eat whitespace.  */
   while (locale_isspace (*s))
@@ -212,6 +214,7 @@
   if (*s == '-' || *s == '+')
     ++s;
 
+  saved_errno = errno;
   num = underlying_strtod (s, &endbuf);
   end = endbuf;
 
@@ -239,6 +242,35 @@
                 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;
     }
--- a/tests/test-strtod.c
+++ b/tests/test-strtod.c
@@ -558,6 +558,26 @@
     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 @@
     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.  */
   {