changeset 33346:5fed83ae6fc3

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 b86c7675b926
children 52672917d131
files ChangeLog lib/strtod.c tests/test-strtod.c
diffstat 3 files changed, 68 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Wed Sep 08 08:07:01 2010 -0700
+++ b/ChangeLog	Wed Sep 01 21:34:44 2010 -0600
@@ -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	Wed Sep 08 08:07:01 2010 -0700
+++ b/lib/strtod.c	Wed Sep 01 21:34:44 2010 -0600
@@ -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	Wed Sep 08 08:07:01 2010 -0700
+++ b/tests/test-strtod.c	Wed Sep 01 21:34:44 2010 -0600
@@ -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.  */
   {