fix various bugs in strtold:

0e10000000000000000000000000000000 was setting ERANGE

exponent char e/p was considered part of the match even if not
followed by a valid decimal value

"1e +10" was parsed as "1e+10"

hex digits were misinterpreted as 0..5 instead of 10..15
This commit is contained in:
Rich Felker 2011-04-03 18:44:37 -04:00
parent c68b26369e
commit e898a79053
1 changed files with 13 additions and 10 deletions

View File

@ -2,6 +2,11 @@
#include <errno.h> #include <errno.h>
#include <ctype.h> #include <ctype.h>
static int valid_exp(const unsigned char *s)
{
return isdigit(*s) || ((s[0]=='+'||s[0]=='-') && isdigit(s[1]));
}
long double strtold(const char *s1, char **p) long double strtold(const char *s1, char **p)
{ {
const unsigned char *s = (void *)s1; const unsigned char *s = (void *)s1;
@ -11,6 +16,7 @@ long double strtold(const char *s1, char **p)
int nonzero = 0; int nonzero = 0;
int radix = '.'; int radix = '.';
long e; long e;
int saved_errno = errno;
if (!p) p = (char **)&s1; if (!p) p = (char **)&s1;
@ -41,26 +47,23 @@ long double strtold(const char *s1, char **p)
/* We have a real hex float */ /* We have a real hex float */
s += 2; s += 2;
for (; isxdigit(*s); s++) { for (; isxdigit(*s); s++) {
x = 16*x + (isdigit(*s)?*s-'0':(*s|32)-'a'); x = 16*x + (isdigit(*s)?*s-'0':(*s|32)-'a'+10);
if (*s!='0') nonzero=1; if (*s!='0') nonzero=1;
} }
if (*s == radix) { if (*s == radix) {
frac = 1.0/16.0; frac = 1.0/16.0;
for (s++; isxdigit(*s); s++) { for (s++; isxdigit(*s); s++) {
x += frac * (isdigit(*s)?*s-'0':(*s|32)-'a'); x += frac * (isdigit(*s)?*s-'0':(*s|32)-'a'+10);
frac *= 1.0/16.0; frac *= 1.0/16.0;
if (*s!='0') nonzero=1; if (*s!='0') nonzero=1;
} }
} }
if ((*s|32) == 'p') { if ((*s|32) == 'p' && valid_exp(s+1)) {
e = strtol((void *)(s+1), (void *)&s, 10); e = strtol((void *)(s+1), (void *)&s, 10);
for (; e>0; e--) x *= 2.0; for (; e>0; e--) x *= 2.0;
for (; e<0; e++) x *= 0.5; for (; e<0; e++) x *= 0.5;
} }
if ((nonzero && !x) || !(1.0/x)) goto finish;
errno = ERANGE;
*p = (char *)s;
return sign ? -x : x;
} }
/* Mantissa must be non-degenerate */ /* Mantissa must be non-degenerate */
@ -81,13 +84,13 @@ long double strtold(const char *s1, char **p)
if (*s!='0') nonzero=1; if (*s!='0') nonzero=1;
} }
} }
if ((*s|32)=='e') { if ((*s|32)=='e' && valid_exp(s+1)) {
e = strtol((void *)++s, (void *)&s, 10); e = strtol((void *)++s, (void *)&s, 10);
for (; e>0; e--) x *= 10.0; for (; e>0; e--) x *= 10.0;
for (; e<0; e++) x /= 10.0; for (; e<0; e++) x /= 10.0;
} }
if ((nonzero && !x) || !(1.0/x)) finish:
errno = ERANGE; errno = ((nonzero && !x) || !(1.0/x)) ? ERANGE : saved_errno;
*p = (char*)s; *p = (char*)s;
return sign ? -x : x; return sign ? -x : x;
} }