X-Git-Url: http://erislabs.net/gitweb/?a=blobdiff_plain;f=lib%2Fstrtod.c;h=a2a4d3db65d82008dc9ba249e034b62f0a15d7f2;hb=8ec8f4b42e39d3aaf2dda06c38b8db00efcc69f5;hp=0d364f23842238bea2a5009b57940d481dfb66e3;hpb=fd6a92931bd9aa252d353d585b10357fd73214ca;p=gnulib.git diff --git a/lib/strtod.c b/lib/strtod.c index 0d364f238..a2a4d3db6 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 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,53 +12,35 @@ 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 -#ifndef errno -extern int errno; -#endif +#include #include -#include - -#ifdef HAVE_FLOAT_H +#include #include -#else -#define DBL_MAX 1.7976931348623159e+308 -#define DBL_MIN 2.2250738585072010e-308 -#endif - -#if STDC_HEADERS -#include +#include +#include #include -#else -#define NULL 0 -#ifndef HUGE_VAL -#define HUGE_VAL HUGE -#endif -#endif + +#include "c-ctype.h" /* 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; +strtod (const char *nptr, char **endptr) { - register const char *s; - short int sign; + const unsigned char *s; + bool negative = false; /* The number so far. */ double num; - int got_dot; /* Found a decimal point. */ - int got_digit; /* Seen any digits. */ + bool got_dot; /* Found a decimal point. */ + bool got_digit; /* Seen any digits. */ + bool hex = false; /* Look for hex float exponent. */ /* The exponent of the number. */ long int exponent; @@ -68,92 +51,182 @@ strtod (nptr, endptr) goto noconv; } - s = nptr; + /* Use unsigned char for the ctype routines. */ + s = (unsigned char *) nptr; /* Eat whitespace. */ while (isspace (*s)) ++s; /* Get the sign. */ - sign = *s == '-' ? -1 : 1; + negative = *s == '-'; if (*s == '-' || *s == '+') ++s; num = 0.0; - got_dot = 0; - got_digit = 0; + got_dot = false; + got_digit = false; exponent = 0; - for (;; ++s) + + /* Check for hex float. */ + if (*s == '0' && c_tolower (s[1]) == 'x' + && (c_isxdigit (s[2]) || ('.' == s[2] && c_isxdigit (s[3])))) { - if (isdigit (*s)) + hex = true; + s += 2; + for (;; ++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; + if (c_isxdigit (*s)) + { + got_digit = true; + + /* Make sure that multiplication by 16 will not overflow. */ + if (num > DBL_MAX / 16) + /* 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 16 later. */ + ++exponent; + else + num = ((num * 16.0) + + (c_tolower (*s) - (c_isdigit (*s) ? '0' : 'a' - 10))); + + /* Keep track of the number of digits after the decimal point. + If we just divided by 16 here, we would lose precision. */ + if (got_dot) + --exponent; + } + else if (!got_dot && *s == '.') + /* Record that we have found the decimal point. */ + got_dot = true; else - num = (num * 10.0) + (*s - '0'); + /* Any other character terminates the number. */ + break; + } + } - /* 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; + /* Not a hex float. */ + else + { + for (;; ++s) + { + if (c_isdigit (*s)) + { + got_digit = true; + + /* 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 = true; + else + /* Any other character terminates the number. */ + break; } - else if (!got_dot && *s == '.') - /* Record that we have found the decimal point. */ - got_dot = 1; - else - /* Any other character terminates the number. */ - break; } if (!got_digit) - goto noconv; + { + /* Check for infinities and NaNs. */ + if (c_tolower (*s) == 'i' + && c_tolower (s[1]) == 'n' + && c_tolower (s[2]) == 'f') + { + s += 3; + num = HUGE_VAL; + 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; + goto valid; + } +#ifdef NAN + else if (c_tolower (*s) == 'n' + && c_tolower (s[1]) == 'a' + && c_tolower (s[2]) == 'n') + { + s += 3; + num = NAN; + /* Since nan() is implementation-defined, + we define it by ignoring . A nicer + implementation would populate the bits of the NaN + according to interpreting n-char-sequence as a + hexadecimal number, but the result is still a NaN. */ + if (*s == '(') + { + const unsigned char *p = s + 1; + while (c_isalnum (*p)) + p++; + if (*p == ')') + s = p + 1; + } + goto valid; + } +#endif + goto noconv; + } - if (tolower (*s) == 'e') + if (c_tolower (*s) == (hex ? 'p' : 'e') && !isspace (s[1])) { /* Get the exponent specified after the `e' or `E'. */ int save = errno; char *end; - long int exp; + long int value; errno = 0; ++s; - exp = strtol (s, &end, 10); - if (errno == ERANGE) + value = strtol ((char *) s, &end, 10); + if (errno == ERANGE && num) { /* 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) + if (value < 0) goto underflow; else goto overflow; } - else if (end == s) + else if (end == (char *) s) /* There was no exponent. Reset END to point to the 'e' or 'E', so *ENDPTR will be set there. */ end = (char *) s - 1; errno = save; - s = end; - exponent += exp; + s = (unsigned char *) end; + exponent += value; } - if (endptr != NULL) - *endptr = (char *) s; - if (num == 0.0) - return 0.0; + goto valid; + + if (hex) + { + /* ldexp takes care of range errors. */ + num = ldexp (num, exponent); + goto valid; + } /* Multiply NUM by 10 to the EXPONENT power, checking for overflow and underflow. */ @@ -171,23 +244,29 @@ strtod (nptr, endptr) num *= pow (10.0, (double) exponent); - return num * sign; + valid: + if (endptr != NULL) + *endptr = (char *) s; + return negative ? -num : num; -overflow: + overflow: /* Return an overflow error. */ + if (endptr != NULL) + *endptr = (char *) s; errno = ERANGE; - return HUGE_VAL * sign; + return negative ? -HUGE_VAL : HUGE_VAL; -underflow: + underflow: /* Return an underflow error. */ if (endptr != NULL) - *endptr = (char *) nptr; + *endptr = (char *) s; errno = ERANGE; - return 0.0; + return negative ? -0.0 : 0.0; -noconv: + noconv: /* There was no number. */ if (endptr != NULL) *endptr = (char *) nptr; + errno = EINVAL; return 0.0; }