From 74faf641ba38dfc7d53b2d5b55cdf779ada920e6 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Wed, 1 Sep 2010 21:34:44 -0600 Subject: [PATCH] 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 --- ChangeLog | 6 ++++++ lib/strtod.c | 32 ++++++++++++++++++++++++++++++++ tests/test-strtod.c | 30 ++++++++++++++++++++++++++++++ 3 files changed, 68 insertions(+) diff --git a/ChangeLog b/ChangeLog index d880808cc..9fb8803ce 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,11 @@ 2010-09-08 Eric Blake + 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. diff --git a/lib/strtod.c b/lib/strtod.c index 64b62ffd2..060dffa20 100644 --- a/lib/strtod.c +++ b/lib/strtod.c @@ -24,6 +24,7 @@ #include #include #include +#include #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; } diff --git a/tests/test-strtod.c b/tests/test-strtod.c index 694e15d96..e1d04af3b 100644 --- a/tests/test-strtod.c +++ b/tests/test-strtod.c @@ -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. */ { -- 2.11.0