X-Git-Url: http://erislabs.net/gitweb/?a=blobdiff_plain;f=lib%2Fstrtod.c;h=e554a84fddb48ae8619a60b88cdc142f6c40404d;hb=cd56634a4a8179fd5a4419fbb3e27211b042ab1c;hp=2f6b894cfa7b842445575765cd6bc58e0fdfa128;hpb=28cceeb00cf761fa58eeb204e4885831b4a45dbf;p=gnulib.git diff --git a/lib/strtod.c b/lib/strtod.c index 2f6b894cf..e554a84fd 100644 --- a/lib/strtod.c +++ b/lib/strtod.c @@ -1,9 +1,10 @@ -/* Copyright (C) 1991, 1992 Free Software Foundation, Inc. +/* 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 + This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -11,180 +12,340 @@ 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, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + along with this program. If not, see . */ -#ifdef HAVE_CONFIG_H #include -#endif -#include +#include + #include +#include +#include +#include #include +#include +#include -#ifdef HAVE_FLOAT_H -#include -#else -#define DBL_MAX 1.7976931348623159e+308 -#define DBL_MIN 2.2250738585072010e-308 -#endif +#include "c-ctype.h" -#if STDC_HEADERS -#include -#include -#else -#define NULL 0 -extern int errno; -#ifndef HUGE_VAL -#define HUGE_VAL HUGE -#endif +#ifndef HAVE_LDEXP_IN_LIBC +#define HAVE_LDEXP_IN_LIBC 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 -strtod (nptr, endptr) - const char *nptr; - char **endptr; +/* Return true if C is a space in the current locale, avoiding + problems with signed char and isspace. */ +static bool +locale_isspace (char c) { - register const char *s; - short int sign; + unsigned char uc = c; + return isspace (uc) != 0; +} - /* The number so far. */ - double num; +#if !HAVE_LDEXP_IN_LIBC + #define ldexp dummy_ldexp + /* A dummy definition that will never be invoked. */ + static double ldexp (double x _GL_UNUSED, int exponent _GL_UNUSED) + { + abort (); + return 0.0; + } +#endif - int got_dot; /* Found a decimal point. */ - int got_digit; /* Seen any digits. */ +/* 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. */ - /* The exponent of the number. */ - long int exponent; + long int e = exponent; - if (nptr == NULL) + if (HAVE_LDEXP_IN_LIBC && radix == 2) + return ldexp (x, e < INT_MIN ? INT_MIN : INT_MAX < e ? INT_MAX : e); + else { - errno = EINVAL; - goto noconv; - } + double r = x; - s = nptr; + 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; + } + } + } - /* Eat whitespace. */ - while (isspace (*s)) - ++s; + return r; + } +} - /* Get the sign. */ - sign = *s == '-' ? -1 : 1; - if (*s == '-' || *s == '+') - ++s; +/* 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; - num = 0.0; - got_dot = 0; - got_digit = 0; - exponent = 0; for (;; ++s) { - if (isdigit (*s)) - { - got_digit = 1; - - /* Make sure that multiplication by 10 will not overflow. */ - if (num > DBL_MAX * 0.1) - /* 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; - else - num = (num * 10.0) + (*s - '0'); - - /* Keep track of the number of digits after the decimal point. - If we just divided by 10 here, we would lose precision. */ - if (got_dot) - --exponent; - } - else if (!got_dot && *s == '.') - /* Record that we have found the decimal point. */ - got_dot = 1; + 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; - } + /* 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. - if (!got_digit) - goto noconv; + We just need to record that there was another + digit so that we can multiply by 10 later. */ + exponent += radix_multiplier; + } - if (tolower (*s) == 'e') + /* 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])) { - /* Get the exponent specified after the `e' or `E'. */ + /* Add any given exponent to the implicit one. */ int save = errno; char *end; - long int exp; - - errno = 0; - ++s; - exp = strtol (s, &end, 10); - if (errno == ERANGE) - { - /* The exponent overflowed a `long int'. It is probably a safe - assumption that an exponent that cannot be represented by - a `long int' exceeds the limits of a `double'. */ - if (endptr != NULL) - *endptr = end; - if (exp < 0) - goto underflow; - else - goto overflow; - } - else if (end == s) - /* There was no exponent. Reset END to point to - the 'e' or 'E', so *ENDPTR will be set there. */ - end = (char *) s - 1; + long int value = strtol (s + 1, &end, 10); errno = save; - s = end; - exponent += exp; + + 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)); + } } - if (endptr != NULL) - *endptr = (char *) s; + *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 + +/* 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) +{ + bool negative = false; + + /* The number so far. */ + double num; - if (num == 0.0) - return 0.0; + const char *s = nptr; + const char *end; + char *endbuf; + int saved_errno; - /* Multiply NUM by 10 to the EXPONENT power, - checking for overflow and underflow. */ + /* Eat whitespace. */ + while (locale_isspace (*s)) + ++s; - if (exponent < 0) + /* Get the sign. */ + negative = *s == '-'; + if (*s == '-' || *s == '+') + ++s; + + saved_errno = errno; + num = underlying_strtod (s, &endbuf); + end = endbuf; + + if (c_isdigit (s[*s == '.'])) { - if (num < DBL_MIN * pow (10.0, (double) -exponent)) - goto underflow; + /* If a hex float was converted incorrectly, do it ourselves. + 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] == '.')])) + 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; } - else if (exponent > 0) + + /* Check for infinities and NaNs. */ + else if (c_tolower (*s) == 'i' + && c_tolower (s[1]) == 'n' + && c_tolower (s[2]) == 'f') { - if (num > DBL_MAX * pow (10.0, (double) -exponent)) - goto overflow; + s += 3; + if (c_tolower (*s) == 'i' + && c_tolower (s[1]) == 'n' + && c_tolower (s[2]) == 'i' + && c_tolower (s[3]) == 't' + && c_tolower (s[4]) == 'y') + s += 5; + num = HUGE_VAL; + errno = saved_errno; } + else if (c_tolower (*s) == 'n' + && c_tolower (s[1]) == 'a' + && c_tolower (s[2]) == 'n') + { + s += 3; + if (*s == '(') + { + const char *p = s + 1; + while (c_isalnum (*p)) + p++; + if (*p == ')') + s = p + 1; + } - num *= pow (10.0, (double) exponent); - - return num * sign; - -overflow: - /* Return an overflow error. */ - errno = ERANGE; - return HUGE_VAL * sign; + /* If the underlying implementation misparsed the NaN, assume + its result is incorrect, and return a NaN. Normally it's + 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) + num = NAN; + errno = saved_errno; + } + else + { + /* No conversion could be performed. */ + errno = EINVAL; + s = nptr; + } -underflow: - /* Return an underflow error. */ if (endptr != NULL) - *endptr = (char *) nptr; - errno = ERANGE; - return 0.0; + *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; +} -noconv: - /* There was no number. */ - if (endptr != NULL) - *endptr = (char *) nptr; - return 0.0; +/* The underlying strtod implementation. This must be defined + after strtod because it #undefs strtod. */ +static double +underlying_strtod (const char *nptr, char **endptr) +{ +#undef strtod + return strtod (nptr, endptr); }