+/* 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)
+{
+ /* If RADIX == 10, this code is neither precise nor fast; it is
+ merely a straightforward and relatively portable approximation.
+ If N == 2, this code is precise on a radix-2 implementation,
+ albeit perhaps not fast if ldexp is not in libc. */
+
+ 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);
+ else
+ {
+ double r = x;
+
+ if (r != 0)
+ {
+ if (e < 0)
+ {
+ while (e++ != 0)
+ {
+ r /= radix;
+ if (r == 0 && x != 0)
+ {
+ errno = ERANGE;
+ break;
+ }
+ }
+ }
+ else
+ {
+ while (e-- != 0)
+ {
+ if (r < -DBL_MAX / radix)
+ {
+ errno = ERANGE;
+ return -HUGE_VAL;
+ }
+ else if (DBL_MAX / radix < r)
+ {
+ errno = ERANGE;
+ return HUGE_VAL;
+ }
+ else
+ r *= radix;
+ }
+ }
+ }
+
+ return r;
+ }
+}
+
+/* Parse a number at NPTR; this is a bit like strtol (NPTR, ENDPTR)
+ 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
+parse_number (const char *nptr,
+ int base, int radix, int radix_multiplier, char expchar,
+ char **endptr)
+{
+ const char *s = nptr;
+ bool got_dot = false;
+ long int exponent = 0;
+ double num = 0;
+
+ 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 == '.')
+ {
+ /* Record that we have found the decimal point. */
+ got_dot = true;
+ continue;
+ }
+ else
+ /* Any other character terminates the number. */
+ break;
+
+ /* Make sure that multiplication by base will not overflow. */
+ if (num <= DBL_MAX / base)
+ num = num * base + digit;
+ else
+ {
+ /* 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;
+ }
+
+ /* 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;
+ }
+
+ if (c_tolower (*s) == expchar && ! locale_isspace (s[1]))
+ {
+ /* Add any given exponent to the implicit one. */
+ int save = errno;
+ char *end;
+ long int value = strtol (s + 1, &end, 10);
+ errno = save;
+
+ if (s + 1 != end)
+ {
+ /* Skip past the exponent, and add in the implicit exponent,
+ resulting in an extreme value on overflow. */
+ s = end;
+ exponent =
+ (exponent < 0
+ ? (value < LONG_MIN - exponent ? LONG_MIN : exponent + value)
+ : (LONG_MAX - exponent < value ? LONG_MAX : exponent + value));
+ }
+ }
+
+ *endptr = (char *) s;
+ 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 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