X-Git-Url: http://nsz.repo.hu/git/?p=musl;a=blobdiff_plain;f=src%2Finternal%2Ffloatscan.c;h=082512130264c7375daafb58589cd3def53754bf;hp=b2313293b3985b5af6a7a2f1df02b5ebd45af398;hb=8489897e01b8a27c365c9c98b200ee25dc124cb4;hpb=4054da9ba062c694dc4fde5c577fcb6da7743bc9 diff --git a/src/internal/floatscan.c b/src/internal/floatscan.c index b2313293..08251213 100644 --- a/src/internal/floatscan.c +++ b/src/internal/floatscan.c @@ -24,6 +24,8 @@ #define MASK (KMAX-1) +#define CONCAT2(x,y) x ## y +#define CONCAT(x,y) CONCAT2(x,y) static long long scanexp(FILE *f, int pok) { @@ -44,7 +46,7 @@ static long long scanexp(FILE *f, int pok) } for (x=0; c-'0'<10U && x30 || x[0]>>bits==0)) + /* Handle zero specially to avoid nasty special cases later */ + if (!x[0]) return sign * 0.0; + + /* Optimize small integers (w/no exponent) and over/under-flow */ + if (lrp==dc && dc<10 && (bits>30 || x[0]>>bits==0)) return sign * (long double)x[0]; if (lrp > -emin/2) { errno = ERANGE; @@ -127,7 +142,8 @@ static long double decfloat(FILE *f, int c, int bits, int emin, int sign, int po return sign * LDBL_MIN * LDBL_MIN; } - if (k30 || x[0]>>bitlim==0) + return sign * (long double)x[0] * p10s[rp-10]; + } + + /* Align radix point to B1B digit boundary */ if (rp % 9) { - static const int p10s[] = { - 100000000, 10000000, 1000000, 100000, - 10000, 1000, 100, 10 - }; int rpm9 = rp>=0 ? rp%9 : rp%9+9; - int p10 = p10s[rpm9-1]; + int p10 = p10s[8-rpm9]; uint32_t carry = 0; for (k=a; k!=z; k++) { uint32_t tmp = x[k] % p10; @@ -159,7 +181,8 @@ static long double decfloat(FILE *f, int c, int bits, int emin, int sign, int po rp += 9-rpm9; } - while (rp < 9*LD_B1B_DIG || (rp == 9*LD_B1B_DIG && x[0] 9+9*LD_B1B_DIG) sh = 9; e2 += sh; - for (i=0; (k=(a+i & MASK))!=z && i>sh) + carry; carry = (1000000000>>sh) * tmp; @@ -210,7 +234,7 @@ static long double decfloat(FILE *f, int c, int bits, int emin, int sign, int po rp -= 9; } } - if (carry && k==z) { + if (carry) { if ((z+1 & MASK) != a) { x[z] = carry; z = (z+1 & MASK); @@ -218,18 +242,22 @@ static long double decfloat(FILE *f, int c, int bits, int emin, int sign, int po } } + /* Assemble desired bits into floating point variable */ for (y=i=0; i LDBL_MANT_DIG+e2-emin) { bits = LDBL_MANT_DIG+e2-emin; if (bits<0) bits=0; + denormal = 1; } + /* Calculate bias term to force rounding, move out lower bits */ if (bits < LDBL_MANT_DIG) { bias = copysignl(scalbn(1, 2*LDBL_MANT_DIG-bits-1), y); frac = fmodl(y, scalbn(1, LDBL_MANT_DIG-bits)); @@ -237,6 +265,7 @@ static long double decfloat(FILE *f, int c, int bits, int emin, int sign, int po y += bias; } + /* Process tail of decimal input so it can affect rounding */ if ((a+i & MASK) != z) { uint32_t t = x[a+i & MASK]; if (t < 500000000 && (t || (a+i+1 & MASK) != z)) @@ -256,11 +285,18 @@ static long double decfloat(FILE *f, int c, int bits, int emin, int sign, int po y += frac; y -= bias; - y = scalbnl(y, e2); - - if (!y) errno = ERANGE; + if ((e2+LDBL_MANT_DIG & INT_MAX) > emax-5) { + if (fabs(y) >= CONCAT(0x1p, LDBL_MANT_DIG)) { + if (denormal && bits==LDBL_MANT_DIG+e2-emin) + denormal = 0; + y *= 0.5; + e2++; + } + if (e2+LDBL_MANT_DIG>emax || (denormal && frac)) + errno = ERANGE; + } - return y; + return scalbnl(y, e2); } static long double hexfloat(FILE *f, int bits, int emin, int sign, int pok) @@ -316,7 +352,7 @@ static long double hexfloat(FILE *f, int bits, int emin, int sign, int pok) } else { shlim(f, 0); } - return 0; + return sign * 0.0; } if (!gotrad) rp = dc; while (dc<8) x *= 16, dc++; @@ -375,31 +411,32 @@ static long double hexfloat(FILE *f, int bits, int emin, int sign, int pok) return scalbnl(y, e2); } -long double __floatscan(FILE *f, int c, int prec, int pok) +long double __floatscan(FILE *f, int prec, int pok) { int sign = 1; - int i; + size_t i; int bits; int emin; + int c; switch (prec) { case 0: - bits = 24; - emin = -149; + bits = FLT_MANT_DIG; + emin = FLT_MIN_EXP-bits; break; case 1: - bits = 53; - emin = -1074; + bits = DBL_MANT_DIG; + emin = DBL_MIN_EXP-bits; break; case 2: bits = LDBL_MANT_DIG; - emin = -16445; + emin = LDBL_MIN_EXP-bits; break; default: return 0; } - if (c<0) c = shgetc(f); + while (isspace((c=shgetc(f)))); if (c=='+' || c=='-') { sign -= 2*(c=='-'); @@ -409,14 +446,33 @@ long double __floatscan(FILE *f, int c, int prec, int pok) for (i=0; i<8 && (c|32)=="infinity"[i]; i++) if (i<7) c = shgetc(f); if (i==3 || i==8 || (i>3 && pok)) { - if (i==3) shunget(f); - if (pok) for (; i>3; i--) shunget(f); - else shlim(f, 0); + if (i!=8) { + shunget(f); + if (pok) for (; i>3; i--) shunget(f); + } return sign * INFINITY; } if (!i) for (i=0; i<3 && (c|32)=="nan"[i]; i++) - if (i<3) c = shgetc(f); + if (i<2) c = shgetc(f); if (i==3) { + if (shgetc(f) != '(') { + shunget(f); + return NAN; + } + for (i=1; ; i++) { + c = shgetc(f); + if (c-'0'<10U || c-'A'<26U || c-'a'<26U || c=='_') + continue; + if (c==')') return NAN; + shunget(f); + if (!pok) { + errno = EINVAL; + shlim(f, 0); + return 0; + } + while (i--) shunget(f); + return NAN; + } return NAN; }