Imported Upstream version 1.4.19
[platform/upstream/m4.git] / lib / strtod.c
index bf6955a..dfdc1fc 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (C) 1991-1992, 1997, 1999, 2003, 2006, 2008-2011 Free Software
+/* Copyright (C) 1991-1992, 1997, 1999, 2003, 2006, 2008-2021 Free Software
    Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    GNU General Public License for more details.
 
    You should have received a copy of the GNU General Public License
-   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
 
-#include <config.h>
+#if ! defined USE_LONG_DOUBLE
+# include <config.h>
+#endif
 
+/* Specification.  */
 #include <stdlib.h>
 
-#include <ctype.h>
+#include <ctype.h>      /* isspace() */
 #include <errno.h>
-#include <float.h>
-#include <limits.h>
-#include <math.h>
+#include <float.h>      /* {DBL,LDBL}_{MIN,MAX} */
+#include <limits.h>     /* LONG_{MIN,MAX} */
+#include <locale.h>     /* localeconv() */
+#include <math.h>       /* NAN */
 #include <stdbool.h>
-#include <string.h>
+#include <stdio.h>      /* sprintf() */
+#include <string.h>     /* strdup() */
+#if HAVE_NL_LANGINFO
+# include <langinfo.h>
+#endif
 
 #include "c-ctype.h"
 
-#ifndef HAVE_LDEXP_IN_LIBC
-#define HAVE_LDEXP_IN_LIBC 0
+#undef MIN
+#undef MAX
+#ifdef USE_LONG_DOUBLE
+# define STRTOD strtold
+# define LDEXP ldexpl
+# if defined __hpux && defined __hppa
+   /* We cannot call strtold on HP-UX/hppa, because its return type is a struct,
+      not a 'long double'.  */
+#  define HAVE_UNDERLYING_STRTOD 0
+# elif STRTOLD_HAS_UNDERFLOW_BUG
+   /* strtold would not set errno=ERANGE upon underflow.  */
+#  define HAVE_UNDERLYING_STRTOD 0
+# else
+#  define HAVE_UNDERLYING_STRTOD HAVE_STRTOLD
+# endif
+# define DOUBLE long double
+# define MIN LDBL_MIN
+# define MAX LDBL_MAX
+# define L_(literal) literal##L
+#else
+# define STRTOD strtod
+# define LDEXP ldexp
+# define HAVE_UNDERLYING_STRTOD 1
+# define DOUBLE double
+# define MIN DBL_MIN
+# define MAX DBL_MAX
+# define L_(literal) literal
 #endif
-#ifndef HAVE_RAW_DECL_STRTOD
-#define HAVE_RAW_DECL_STRTOD 0
+
+#if (defined USE_LONG_DOUBLE ? HAVE_LDEXPM_IN_LIBC : HAVE_LDEXP_IN_LIBC)
+# define USE_LDEXP 1
+#else
+# define USE_LDEXP 0
 #endif
 
 /* Return true if C is a space in the current locale, avoiding
@@ -44,20 +80,43 @@ locale_isspace (char c)
   return isspace (uc) != 0;
 }
 
-#if !HAVE_LDEXP_IN_LIBC
- #define ldexp dummy_ldexp
+/* Determine the decimal-point character according to the current locale.  */
+static char
+decimal_point_char (void)
+{
+  const char *point;
+  /* Determine it in a multithread-safe way.  We know nl_langinfo is
+     multithread-safe on glibc systems and Mac OS X systems, but is not required
+     to be multithread-safe by POSIX.  sprintf(), however, is multithread-safe.
+     localeconv() is rarely multithread-safe.  */
+#if HAVE_NL_LANGINFO && (__GLIBC__ || defined __UCLIBC__ || (defined __APPLE__ && defined __MACH__))
+  point = nl_langinfo (RADIXCHAR);
+#elif 1
+  char pointbuf[5];
+  sprintf (pointbuf, "%#.0f", 1.0);
+  point = &pointbuf[1];
+#else
+  point = localeconv () -> decimal_point;
+#endif
+  /* The decimal point is always a single byte: either '.' or ','.  */
+  return (point[0] != '\0' ? point[0] : '.');
+}
+
+#if !USE_LDEXP
+ #undef LDEXP
+ #define LDEXP dummy_ldexp
  /* A dummy definition that will never be invoked.  */
- static double ldexp (double x _GL_UNUSED, int exponent _GL_UNUSED)
+ static DOUBLE LDEXP (DOUBLE x _GL_UNUSED, int exponent _GL_UNUSED)
  {
    abort ();
-   return 0.0;
+   return L_(0.0);
  }
 #endif
 
 /* Return X * BASE**EXPONENT.  Return an extreme value and set errno
    to ERANGE if underflow or overflow occurs.  */
-static double
-scale_radix_exp (double x, int radix, long int exponent)
+static DOUBLE
+scale_radix_exp (DOUBLE x, int radix, long int exponent)
 {
   /* If RADIX == 10, this code is neither precise nor fast; it is
      merely a straightforward and relatively portable approximation.
@@ -66,11 +125,11 @@ scale_radix_exp (double x, int radix, long int exponent)
 
   long int e = exponent;
 
-  if (HAVE_LDEXP_IN_LIBC && radix == 2)
-    return ldexp (x, e < INT_MIN ? INT_MIN : INT_MAX < e ? INT_MAX : e);
+  if (USE_LDEXP && radix == 2)
+    return LDEXP (x, e < INT_MIN ? INT_MIN : INT_MAX < e ? INT_MAX : e);
   else
     {
-      double r = x;
+      DOUBLE r = x;
 
       if (r != 0)
         {
@@ -90,12 +149,12 @@ scale_radix_exp (double x, int radix, long int exponent)
             {
               while (e-- != 0)
                 {
-                  if (r < -DBL_MAX / radix)
+                  if (r < -MAX / radix)
                     {
                       errno = ERANGE;
                       return -HUGE_VAL;
                     }
-                  else if (DBL_MAX / radix < r)
+                  else if (MAX / radix < r)
                     {
                       errno = ERANGE;
                       return HUGE_VAL;
@@ -114,63 +173,109 @@ scale_radix_exp (double x, int radix, long int exponent)
    except there are no leading spaces or signs or "0x", and ENDPTR is
    nonnull.  The number uses a base BASE (either 10 or 16) fraction, a
    radix RADIX (either 10 or 2) exponent, and exponent character
-   EXPCHAR.  To convert from a number of digits to a radix exponent,
-   multiply by RADIX_MULTIPLIER (either 1 or 4).  */
-static double
+   EXPCHAR.  BASE is RADIX**RADIX_MULTIPLIER.  */
+static DOUBLE
 parse_number (const char *nptr,
-              int base, int radix, int radix_multiplier, char expchar,
+              int base, int radix, int radix_multiplier, char radixchar,
+              char expchar,
               char **endptr)
 {
   const char *s = nptr;
-  bool got_dot = false;
-  long int exponent = 0;
-  double num = 0;
-
+  const char *digits_start;
+  const char *digits_end;
+  const char *radixchar_ptr;
+  long int exponent;
+  DOUBLE num;
+
+  /* First, determine the start and end of the digit sequence.  */
+  digits_start = s;
+  radixchar_ptr = NULL;
   for (;; ++s)
     {
-      int digit;
-      if (c_isdigit (*s))
-        digit = *s - '0';
-      else if (base == 16 && c_isxdigit (*s))
-        digit = c_tolower (*s) - ('a' - 10);
-      else if (! got_dot && *s == '.')
+      if (base == 16 ? c_isxdigit (*s) : c_isdigit (*s))
+        ;
+      else if (radixchar_ptr == NULL && *s == radixchar)
         {
           /* Record that we have found the decimal point.  */
-          got_dot = true;
-          continue;
+          radixchar_ptr = s;
         }
       else
-        /* Any other character terminates the number.  */
+        /* Any other character terminates the digit sequence.  */
         break;
+    }
+  digits_end = s;
+  /* Now radixchar_ptr == NULL or
+     digits_start <= radixchar_ptr < digits_end.  */
+
+  if (false)
+    { /* Unoptimized.  */
+      exponent =
+        (radixchar_ptr != NULL
+         ? - (long int) (digits_end - radixchar_ptr - 1)
+         : 0);
+    }
+  else
+    { /* Remove trailing zero digits.  This reduces rounding errors for
+         inputs such as 1.0000000000 or 10000000000e-10.  */
+      while (digits_end > digits_start)
+        {
+          if (digits_end - 1 == radixchar_ptr || *(digits_end - 1) == '0')
+            digits_end--;
+          else
+            break;
+        }
+      exponent =
+        (radixchar_ptr != NULL
+         ? (digits_end > radixchar_ptr
+            ? - (long int) (digits_end - radixchar_ptr - 1)
+            : (long int) (radixchar_ptr - digits_end))
+         : (long int) (s - digits_end));
+    }
 
-      /* Make sure that multiplication by base will not overflow.  */
-      if (num <= DBL_MAX / base)
-        num = num * base + digit;
-      else
+  /* Then, convert the digit sequence to a number.  */
+  {
+    const char *dp;
+    num = 0;
+    for (dp = digits_start; dp < digits_end; dp++)
+      if (dp != radixchar_ptr)
         {
-          /* The value of the digit doesn't matter, since we have already
-             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.
-
-             We just need to record that there was another
-             digit so that we can multiply by 10 later.  */
-          exponent += radix_multiplier;
+          int digit;
+
+          /* Make sure that multiplication by BASE will not overflow.  */
+          if (!(num <= MAX / base))
+            {
+              /* The value of the digit and all subsequent digits don't matter,
+                 since we have already gotten as many digits as can be
+                 represented in a 'DOUBLE'.  This doesn't necessarily mean that
+                 the result will overflow: The exponent may reduce it to within
+                 range.  */
+              exponent +=
+                (digits_end - dp)
+                - (radixchar_ptr >= dp && radixchar_ptr < digits_end ? 1 : 0);
+              break;
+            }
+
+          /* Eat the next digit.  */
+          if (c_isdigit (*dp))
+            digit = *dp - '0';
+          else if (base == 16 && c_isxdigit (*dp))
+            digit = c_tolower (*dp) - ('a' - 10);
+          else
+            abort ();
+          num = num * base + digit;
         }
+  }
 
-      /* Keep track of the number of digits after the decimal point.
-         If we just divided by base here, we might lose precision.  */
-      if (got_dot)
-        exponent -= radix_multiplier;
-    }
+  exponent = exponent * radix_multiplier;
 
+  /* Finally, parse the exponent.  */
   if (c_tolower (*s) == expchar && ! locale_isspace (s[1]))
     {
       /* Add any given exponent to the implicit one.  */
-      int save = errno;
+      int saved_errno = errno;
       char *end;
       long int value = strtol (s + 1, &end, 10);
-      errno = save;
+      errno = saved_errno;
 
       if (s + 1 != end)
         {
@@ -188,37 +293,50 @@ parse_number (const char *nptr,
   return scale_radix_exp (num, radix, exponent);
 }
 
-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 MacOS X 10.5.  */
-#if defined __hpux || defined __sgi || defined __ICC
-static double
-compute_minus_zero (void)
+   The expression -MIN * MIN does not work when cross-compiling
+   to PowerPC on Mac OS X 10.5.  */
+static DOUBLE
+minus_zero (void)
 {
-  return -DBL_MIN * DBL_MIN;
-}
-# define minus_zero compute_minus_zero ()
+#if defined __hpux || defined __sgi || defined __ICC
+  return -MIN * MIN;
 #else
-double minus_zero = -0.0;
+  return -0.0;
 #endif
+}
 
-/* Convert NPTR to a double.  If ENDPTR is not NULL, a pointer to the
+/* 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
-strtod (const char *nptr, char **endptr)
+DOUBLE
+STRTOD (const char *nptr, char **endptr)
+#if HAVE_UNDERLYING_STRTOD
+# ifdef USE_LONG_DOUBLE
+#  undef strtold
+# else
+#  undef strtod
+# endif
+#else
+# undef STRTOD
+# define STRTOD(NPTR,ENDPTR) \
+   parse_number (NPTR, 10, 10, 1, radixchar, 'e', ENDPTR)
+#endif
+/* From here on, STRTOD refers to the underlying implementation.  It needs
+   to handle only finite unsigned decimal numbers with non-null ENDPTR.  */
 {
+  char radixchar;
   bool negative = false;
 
   /* The number so far.  */
-  double num;
+  DOUBLE num;
 
   const char *s = nptr;
   const char *end;
   char *endbuf;
-  int saved_errno;
+  int saved_errno = errno;
+
+  radixchar = decimal_point_char ();
 
   /* Eat whitespace.  */
   while (locale_isspace (*s))
@@ -229,11 +347,10 @@ strtod (const char *nptr, char **endptr)
   if (*s == '-' || *s == '+')
     ++s;
 
-  saved_errno = errno;
-  num = underlying_strtod (s, &endbuf);
+  num = STRTOD (s, &endbuf);
   end = endbuf;
 
-  if (c_isdigit (s[*s == '.']))
+  if (c_isdigit (s[*s == radixchar]))
     {
       /* If a hex float was converted incorrectly, do it ourselves.
          If the string starts with "0x" but does not contain digits,
@@ -241,11 +358,16 @@ strtod (const char *nptr, char **endptr)
          'p' but no exponent, then adjust the end pointer.  */
       if (*s == '0' && c_tolower (s[1]) == 'x')
         {
-          if (! c_isxdigit (s[2 + (s[2] == '.')]))
-            end = s + 1;
+          if (! c_isxdigit (s[2 + (s[2] == radixchar)]))
+            {
+              end = s + 1;
+
+              /* strtod() on z/OS returns ERANGE for "0x".  */
+              errno = saved_errno;
+            }
           else if (end <= s + 2)
             {
-              num = parse_number (s + 2, 16, 2, 4, 'p', &endbuf);
+              num = parse_number (s + 2, 16, 2, 4, radixchar, 'p', &endbuf);
               end = endbuf;
             }
           else
@@ -254,13 +376,32 @@ strtod (const char *nptr, char **endptr)
               while (p < end && c_tolower (*p) != 'p')
                 p++;
               if (p < end && ! c_isdigit (p[1 + (p[1] == '-' || p[1] == '+')]))
-                end = p;
+                {
+                  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 + 2, 16, 2, 4, radixchar, 'p', &endbuf);
+                    }
+                  else
+                    {
+                      dup[p - s] = '\0';
+                      num = STRTOD (dup, &endbuf);
+                      saved_errno = errno;
+                      free (dup);
+                      errno = saved_errno;
+                    }
+                  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
+             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')
@@ -273,12 +414,12 @@ strtod (const char *nptr, char **endptr)
                 {
                   /* Not really our day, is it.  Rounding errors are
                      better than outright failure.  */
-                  num = parse_number (s, 10, 10, 1, 'e', &endbuf);
+                  num = parse_number (s, 10, 10, 1, radixchar, 'e', &endbuf);
                 }
               else
                 {
                   dup[e - s] = '\0';
-                  num = underlying_strtod (dup, &endbuf);
+                  num = STRTOD (dup, &endbuf);
                   saved_errno = errno;
                   free (dup);
                   errno = saved_errno;
@@ -324,7 +465,7 @@ strtod (const char *nptr, char **endptr)
          better to use the underlying implementation's result, since a
          nice implementation populates the bits of the NaN according
          to interpreting n-char-sequence as a hexadecimal number.  */
-      if (s != end)
+      if (s != end || num == num)
         num = NAN;
       errno = saved_errno;
     }
@@ -340,28 +481,6 @@ strtod (const char *nptr, char **endptr)
   /* 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 minus_zero ();
   return negative ? -num : num;
 }
-
-/* The "underlying" strtod implementation.  This must be defined
-   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);
-    }
-}