X-Git-Url: http://erislabs.net/gitweb/?a=blobdiff_plain;f=lib%2Fstrtod.c;h=bf6955a7783fb28017e7eaae50aa697dc32c9230;hb=1bb384c44db25aef8f8b455cef22af32d8401abc;hp=bd0ff43e7417d6ad0b9672a02a69290081503e06;hpb=f4c549fab688ce822556eadcf60fdda5042ade11;p=gnulib.git diff --git a/lib/strtod.c b/lib/strtod.c index bd0ff43e7..bf6955a77 100644 --- a/lib/strtod.c +++ b/lib/strtod.c @@ -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-2011 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify @@ -24,6 +24,7 @@ #include #include #include +#include #include "c-ctype.h" @@ -189,6 +190,21 @@ parse_number (const char *nptr, 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) +{ + 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 @@ -200,7 +216,9 @@ strtod (const char *nptr, char **endptr) double num; const char *s = nptr; - char *end; + const char *end; + char *endbuf; + int saved_errno; /* Eat whitespace. */ while (locale_isspace (*s)) @@ -211,18 +229,62 @@ strtod (const char *nptr, char **endptr) 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 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; + 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; @@ -241,6 +303,7 @@ strtod (const char *nptr, char **endptr) && c_tolower (s[4]) == 'y') s += 5; num = HUGE_VAL; + errno = saved_errno; } else if (c_tolower (*s) == 'n' && c_tolower (s[1]) == 'a' @@ -263,6 +326,7 @@ strtod (const char *nptr, char **endptr) to interpreting n-char-sequence as a hexadecimal number. */ if (s != end) num = NAN; + errno = saved_errno; } else { @@ -273,6 +337,10 @@ strtod (const char *nptr, char **endptr) 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; }