Merge commit 'a39d4083cab589d7cd6a13e8a4b8db8875261d75'
[gnulib.git] / lib / strtod.c
index bd0ff43..e554a84 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (C) 1991-1992, 1997, 1999, 2003, 2006, 2008-2010 Free Software
+/* Copyright (C) 1991-1992, 1997, 1999, 2003, 2006, 2008-2014 Free Software
    Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
 #include <limits.h>
 #include <math.h>
 #include <stdbool.h>
 #include <limits.h>
 #include <math.h>
 #include <stdbool.h>
+#include <string.h>
 
 #include "c-ctype.h"
 
 #ifndef HAVE_LDEXP_IN_LIBC
 #define HAVE_LDEXP_IN_LIBC 0
 #endif
 
 #include "c-ctype.h"
 
 #ifndef HAVE_LDEXP_IN_LIBC
 #define HAVE_LDEXP_IN_LIBC 0
 #endif
-#ifndef HAVE_RAW_DECL_STRTOD
-#define HAVE_RAW_DECL_STRTOD 0
-#endif
 
 /* Return true if C is a space in the current locale, avoiding
    problems with signed char and isspace.  */
 
 /* Return true if C is a space in the current locale, avoiding
    problems with signed char and isspace.  */
@@ -148,7 +146,7 @@ parse_number (const char *nptr,
       else
         {
           /* The value of the digit doesn't matter, since we have already
       else
         {
           /* The value of the digit doesn't matter, since we have already
-             gotten as many digits as can be represented in a `double'.
+             gotten as many digits as can be represented in a 'double'.
              This doesn't necessarily mean the result will overflow.
              The exponent may reduce it to within range.
 
              This doesn't necessarily mean the result will overflow.
              The exponent may reduce it to within range.
 
@@ -189,6 +187,21 @@ parse_number (const char *nptr,
 
 static double underlying_strtod (const char *, char **);
 
 
 static double underlying_strtod (const char *, char **);
 
+/* HP cc on HP-UX 10.20 has a bug with the constant expression -0.0.
+   ICC 10.0 has a bug when optimizing the expression -zero.
+   The expression -DBL_MIN * DBL_MIN does not work when cross-compiling
+   to PowerPC on Mac OS X 10.5.  */
+#if defined __hpux || defined __sgi || defined __ICC
+static double
+compute_minus_zero (void)
+{
+  return -DBL_MIN * DBL_MIN;
+}
+# define minus_zero compute_minus_zero ()
+#else
+double minus_zero = -0.0;
+#endif
+
 /* Convert NPTR to a double.  If ENDPTR is not NULL, a pointer to the
    character after the last one used in the number is put in *ENDPTR.  */
 double
 /* Convert NPTR to a double.  If ENDPTR is not NULL, a pointer to the
    character after the last one used in the number is put in *ENDPTR.  */
 double
@@ -200,7 +213,9 @@ strtod (const char *nptr, char **endptr)
   double num;
 
   const char *s = nptr;
   double num;
 
   const char *s = nptr;
-  char *end;
+  const char *end;
+  char *endbuf;
+  int saved_errno;
 
   /* Eat whitespace.  */
   while (locale_isspace (*s))
 
   /* Eat whitespace.  */
   while (locale_isspace (*s))
@@ -211,18 +226,62 @@ strtod (const char *nptr, char **endptr)
   if (*s == '-' || *s == '+')
     ++s;
 
   if (*s == '-' || *s == '+')
     ++s;
 
-  num = underlying_strtod (s, &end);
+  saved_errno = errno;
+  num = underlying_strtod (s, &endbuf);
+  end = endbuf;
 
   if (c_isdigit (s[*s == '.']))
     {
       /* If a hex float was converted incorrectly, do it ourselves.
 
   if (c_isdigit (s[*s == '.']))
     {
       /* If a hex float was converted incorrectly, do it ourselves.
-         If the string starts with "0x", consume the "0" ourselves.  */
-      if (*s == '0' && c_tolower (s[1]) == 'x' && end <= s + 2)
+         If the string starts with "0x" but does not contain digits,
+         consume the "0" ourselves.  If a hex float is followed by a
+         'p' but no exponent, then adjust the end pointer.  */
+      if (*s == '0' && c_tolower (s[1]) == 'x')
         {
         {
-          if (c_isxdigit (s[2 + (s[2] == '.')]))
-            num = parse_number (s + 2, 16, 2, 4, 'p', &end);
-          else
+          if (! c_isxdigit (s[2 + (s[2] == '.')]))
             end = s + 1;
             end = s + 1;
+          else if (end <= s + 2)
+            {
+              num = parse_number (s + 2, 16, 2, 4, 'p', &endbuf);
+              end = endbuf;
+            }
+          else
+            {
+              const char *p = s + 2;
+              while (p < end && c_tolower (*p) != 'p')
+                p++;
+              if (p < end && ! c_isdigit (p[1 + (p[1] == '-' || p[1] == '+')]))
+                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;
         }
 
       s = end;
@@ -241,6 +300,7 @@ strtod (const char *nptr, char **endptr)
           && c_tolower (s[4]) == 'y')
         s += 5;
       num = HUGE_VAL;
           && c_tolower (s[4]) == 'y')
         s += 5;
       num = HUGE_VAL;
+      errno = saved_errno;
     }
   else if (c_tolower (*s) == 'n'
            && c_tolower (s[1]) == 'a'
     }
   else if (c_tolower (*s) == 'n'
            && c_tolower (s[1]) == 'a'
@@ -263,6 +323,7 @@ strtod (const char *nptr, char **endptr)
          to interpreting n-char-sequence as a hexadecimal number.  */
       if (s != end)
         num = NAN;
          to interpreting n-char-sequence as a hexadecimal number.  */
       if (s != end)
         num = NAN;
+      errno = saved_errno;
     }
   else
     {
     }
   else
     {
@@ -273,27 +334,18 @@ strtod (const char *nptr, char **endptr)
 
   if (endptr != NULL)
     *endptr = (char *) s;
 
   if (endptr != NULL)
     *endptr = (char *) s;
+  /* Special case -0.0, since at least ICC miscompiles negation.  We
+     can't use copysign(), as that drags in -lm on some platforms.  */
+  if (!num && negative)
+    return minus_zero;
   return negative ? -num : num;
 }
 
   return negative ? -num : num;
 }
 
-/* The "underlying" strtod implementation.  This must be defined
+/* The underlying strtod implementation.  This must be defined
    after strtod because it #undefs strtod.  */
 static double
 underlying_strtod (const char *nptr, char **endptr)
 {
    after strtod because it #undefs strtod.  */
 static double
 underlying_strtod (const char *nptr, char **endptr)
 {
-  if (HAVE_RAW_DECL_STRTOD)
-    {
-      /* Prefer the native strtod if available.  Usually it should
-         work and it should give more-accurate results than our
-         approximation.  */
-      #undef strtod
-      return strtod (nptr, endptr);
-    }
-  else
-    {
-      /* Approximate strtod well enough for this module.  There's no
-         need to handle anything but finite unsigned decimal
-         numbers with nonnull ENDPTR.  */
-      return parse_number (nptr, 10, 10, 1, 'e', endptr);
-    }
+#undef strtod
+  return strtod (nptr, endptr);
 }
 }