diff options
author | Mark Dickinson <dickinsm@gmail.com> | 2010-01-20 17:36:31 (GMT) |
---|---|---|
committer | Mark Dickinson <dickinsm@gmail.com> | 2010-01-20 17:36:31 (GMT) |
commit | 4141d65fb7eacccee58b2fa462b56a2410e67777 (patch) | |
tree | 925a2ffd4d06528bbb10a6c06845bdea5221b84c /Python | |
parent | 8926108bfbf0b5827d7ee2c863998793c3162f78 (diff) | |
download | cpython-4141d65fb7eacccee58b2fa462b56a2410e67777.zip cpython-4141d65fb7eacccee58b2fa462b56a2410e67777.tar.gz cpython-4141d65fb7eacccee58b2fa462b56a2410e67777.tar.bz2 |
Various dtoa.c cleanups. 1. Despagghetify _Py_dg_strtod parsing code
and exit points. 2. Simplify bigcomp comparison loop. 3. Don't set
ERANGE on _Py_dg_strtod underflow (it was set inconsistently anyway).
4. Remove unused dsign field from BCinfo struct.
Diffstat (limited to 'Python')
-rw-r--r-- | Python/dtoa.c | 350 |
1 files changed, 165 insertions, 185 deletions
diff --git a/Python/dtoa.c b/Python/dtoa.c index de3ca9e..671d027 100644 --- a/Python/dtoa.c +++ b/Python/dtoa.c @@ -270,7 +270,7 @@ typedef union { double d; ULong L[2]; } U; typedef struct BCinfo BCinfo; struct BCinfo { - int dsign, e0, nd, nd0, scale; + int e0, nd, nd0, scale; }; #define FFFFFFFF 0xffffffffUL @@ -967,8 +967,8 @@ diff(Bigint *a, Bigint *b) return c; } -/* Given a positive normal double x, return the difference between x and the next - double up. Doesn't give correct results for subnormals. */ +/* Given a positive normal double x, return the difference between x and the + next double up. Doesn't give correct results for subnormals. */ static double ulp(U *x) @@ -1276,9 +1276,6 @@ sulp(U *x, BCinfo *bc) bc is a struct containing information gathered during the parsing and estimation steps of _Py_dg_strtod. Description of fields follows: - bc->dsign is 1 if rv < decimal value, 0 if rv >= decimal value. In - normal use, it should almost always be 1 when bigcomp is entered. - bc->e0 gives the exponent of the input value, such that dv = (integer given by the bd->nd digits of s0) * 10**e0 @@ -1387,47 +1384,37 @@ bigcomp(U *rv, const char *s0, BCinfo *bc) } } - /* if b >= d, round down */ - if (cmp(b, d) >= 0) { + /* Compare s0 with b/d: set dd to -1, 0, or 1 according as s0 < b/d, s0 == + * b/d, or s0 > b/d. Here the digits of s0 are thought of as representing + * a number in the range [0.1, 1). */ + if (cmp(b, d) >= 0) + /* b/d >= 1 */ dd = -1; - goto ret; - } + else { + i = 0; + for(;;) { + b = multadd(b, 10, 0); + if (b == NULL) { + Bfree(d); + return -1; + } + dd = s0[i < nd0 ? i : i+1] - '0' - quorem(b, d); + i++; - /* Compare b/d with s0 */ - for(i = 0; i < nd0; i++) { - b = multadd(b, 10, 0); - if (b == NULL) { - Bfree(d); - return -1; - } - dd = *s0++ - '0' - quorem(b, d); - if (dd) - goto ret; - if (!b->x[0] && b->wds == 1) { - if (i < nd - 1) - dd = 1; - goto ret; - } - } - s0++; - for(; i < nd; i++) { - b = multadd(b, 10, 0); - if (b == NULL) { - Bfree(d); - return -1; - } - dd = *s0++ - '0' - quorem(b, d); - if (dd) - goto ret; - if (!b->x[0] && b->wds == 1) { - if (i < nd - 1) - dd = 1; - goto ret; + if (dd) + break; + if (!b->x[0] && b->wds == 1) { + /* b/d == 0 */ + dd = i < nd; + break; + } + if (!(i < nd)) { + /* b/d != 0, but digits of s0 exhausted */ + dd = -1; + break; + } } } - if (b->x[0] || b->wds > 1) - dd = -1; - ret: Bfree(b); Bfree(d); if (dd > 0 || (dd == 0 && odd)) @@ -1438,128 +1425,129 @@ bigcomp(U *rv, const char *s0, BCinfo *bc) double _Py_dg_strtod(const char *s00, char **se) { - int bb2, bb5, bbe, bd2, bd5, bbbits, bs2, c, e, e1, error; - int esign, i, j, k, nd, nd0, nf, nz, nz0, sign; + int bb2, bb5, bbe, bd2, bd5, bbbits, bs2, c, dsign, e, e1, error; + int esign, i, j, k, lz, nd, nd0, sign; const char *s, *s0, *s1; double aadj, aadj1; U aadj2, adj, rv, rv0; - ULong y, z, abse; + ULong y, z, abs_exp; Long L; BCinfo bc; Bigint *bb, *bb1, *bd, *bd0, *bs, *delta; - sign = nz0 = nz = 0; dval(&rv) = 0.; - for(s = s00;;s++) switch(*s) { - case '-': - sign = 1; - /* no break */ - case '+': - if (*++s) - goto break2; - /* no break */ - case 0: - goto ret0; - /* modify original dtoa.c so that it doesn't accept leading whitespace - case '\t': - case '\n': - case '\v': - case '\f': - case '\r': - case ' ': - continue; - */ - default: - goto break2; - } - break2: - if (*s == '0') { - nz0 = 1; - while(*++s == '0') ; - if (!*s) - goto ret; + + /* Start parsing. */ + c = *(s = s00); + + /* Parse optional sign, if present. */ + sign = 0; + switch (c) { + case '-': + sign = 1; + /* no break */ + case '+': + c = *++s; } - s0 = s; - for(nd = nf = 0; (c = *s) >= '0' && c <= '9'; nd++, s++) - ; - nd0 = nd; + + /* Skip leading zeros: lz is true iff there were leading zeros. */ + s1 = s; + while (c == '0') + c = *++s; + lz = s != s1; + + /* Point s0 at the first nonzero digit (if any). nd0 will be the position + of the point relative to s0. nd will be the total number of digits + ignoring leading zeros. */ + s0 = s1 = s; + while ('0' <= c && c <= '9') + c = *++s; + nd0 = nd = s - s1; + + /* Parse decimal point and following digits. */ if (c == '.') { c = *++s; if (!nd) { - for(; c == '0'; c = *++s) - nz++; - if (c > '0' && c <= '9') { - s0 = s; - nf += nz; - nz = 0; - goto have_dig; - } - goto dig_done; - } - for(; c >= '0' && c <= '9'; c = *++s) { - have_dig: - nz++; - if (c -= '0') { - nf += nz; - nd += nz; - nz = 0; - } + s1 = s; + while (c == '0') + c = *++s; + lz = lz || s != s1; + nd0 -= s - s1; + s0 = s; } + s1 = s; + while ('0' <= c && c <= '9') + c = *++s; + nd += s - s1; } - dig_done: + + /* Now lz is true if and only if there were leading zero digits, and nd + gives the total number of digits ignoring leading zeros. A valid input + must have at least one digit. */ + if (!nd && !lz) { + *se = (char *)s00; + goto parse_error; + } + + /* Parse exponent. */ e = 0; if (c == 'e' || c == 'E') { - if (!nd && !nz && !nz0) { - goto ret0; - } s00 = s; + c = *++s; + + /* Exponent sign. */ esign = 0; - switch(c = *++s) { + switch (c) { case '-': esign = 1; + /* no break */ case '+': c = *++s; } - if (c >= '0' && c <= '9') { - while(c == '0') - c = *++s; - if (c > '0' && c <= '9') { - abse = c - '0'; - s1 = s; - while((c = *++s) >= '0' && c <= '9') - abse = 10*abse + c - '0'; - if (s - s1 > 8 || abse > MAX_ABS_EXP) - /* Avoid confusion from exponents - * so large that e might overflow. - */ - e = (int)MAX_ABS_EXP; /* safe for 16 bit ints */ - else - e = (int)abse; - if (esign) - e = -e; - } - else - e = 0; + + /* Skip zeros. lz is true iff there are leading zeros. */ + s1 = s; + while (c == '0') + c = *++s; + lz = s != s1; + + /* Get absolute value of the exponent. */ + s1 = s; + abs_exp = 0; + while ('0' <= c && c <= '9') { + abs_exp = 10*abs_exp + (c - '0'); + c = *++s; } + + /* abs_exp will be correct modulo 2**32. But 10**9 < 2**32, so if + there are at most 9 significant exponent digits then overflow is + impossible. */ + if (s - s1 > 9 || abs_exp > MAX_ABS_EXP) + e = (int)MAX_ABS_EXP; else + e = (int)abs_exp; + if (esign) + e = -e; + + /* A valid exponent must have at least one digit. */ + if (s == s1 && !lz) s = s00; } - if (!nd) { - if (!nz && !nz0) { - ret0: - s = s00; - sign = 0; - } - goto ret; - } - e -= nf; - if (!nd0) + + /* Adjust exponent to take into account position of the point. */ + e -= nd - nd0; + if (nd0 <= 0) nd0 = nd; - /* strip trailing zeros */ + /* Finished parsing. Set se to indicate how far we parsed */ + if (se) + *se = (char *)s; + + /* If all digits were zero, exit with return value +-0.0. Otherwise, + strip trailing zeros: scan back until we hit a nonzero digit. */ + if (!nd) + goto ret; for (i = nd; i > 0; ) { - /* scan back until we hit a nonzero digit. significant digit 'i' - is s0[i] if i < nd0, s0[i+1] if i >= nd0. */ --i; if (s0[i < nd0 ? i : i+1] != '0') { ++i; @@ -1571,28 +1559,21 @@ _Py_dg_strtod(const char *s00, char **se) if (nd0 > nd) nd0 = nd; - /* Now we have nd0 digits, starting at s0, followed by a - * decimal point, followed by nd-nd0 digits. The number we're - * after is the integer represented by those digits times - * 10**e */ - - bc.e0 = e1 = e; - - /* Summary of parsing results. The parsing stage gives values - * s0, nd0, nd, e, sign, where: + /* Summary of parsing results. After parsing, and dealing with zero + * inputs, we have values s0, nd0, nd, e, sign, where: * - * - s0 points to the first significant digit of the input string s00; + * - s0 points to the first significant digit of the input string * * - nd is the total number of significant digits (here, and * below, 'significant digits' means the set of digits of the * significand of the input that remain after ignoring leading - * and trailing zeros. + * and trailing zeros). * - * - nd0 indicates the position of the decimal point (if - * present): so the nd significant digits are in s0[0:nd0] and - * s0[nd0+1:nd+1] using the usual Python half-open slice - * notation. (If nd0 < nd, then s0[nd0] necessarily contains - * a '.' character; if nd0 == nd, then it could be anything.) + * - nd0 indicates the position of the decimal point, if present; it + * satisfies 1 <= nd0 <= nd. The nd significant digits are in + * s0[0:nd0] and s0[nd0+1:nd+1] using the usual Python half-open slice + * notation. (If nd0 < nd, then s0[nd0] contains a '.' character; if + * nd0 == nd, then s0[nd0] could be any non-digit character.) * * - e is the adjusted exponent: the absolute value of the number * represented by the original input string is n * 10**e, where @@ -1614,6 +1595,7 @@ _Py_dg_strtod(const char *s00, char **se) * gives the value represented by the first min(16, nd) sig. digits. */ + bc.e0 = e1 = e; y = z = 0; for (i = 0; i < nd; i++) { if (i < 9) @@ -1666,14 +1648,8 @@ _Py_dg_strtod(const char *s00, char **se) if ((i = e1 & 15)) dval(&rv) *= tens[i]; if (e1 &= ~15) { - if (e1 > DBL_MAX_10_EXP) { - ovfl: - errno = ERANGE; - /* Can't trust HUGE_VAL */ - word0(&rv) = Exp_mask; - word1(&rv) = 0; - goto ret; - } + if (e1 > DBL_MAX_10_EXP) + goto ovfl; e1 >>= 4; for(j = 0; e1 > 1; j++, e1 >>= 1) if (e1 & 1) @@ -1719,12 +1695,8 @@ _Py_dg_strtod(const char *s00, char **se) else word1(&rv) &= 0xffffffff << j; } - if (!dval(&rv)) { - undfl: - dval(&rv) = 0.; - errno = ERANGE; - goto ret; - } + if (!dval(&rv)) + goto undfl; } } @@ -1882,11 +1854,11 @@ _Py_dg_strtod(const char *s00, char **se) Bfree(bd0); goto failed_malloc; } - bc.dsign = delta->sign; + dsign = delta->sign; delta->sign = 0; i = cmp(delta, bs); if (bc.nd > nd && i <= 0) { - if (bc.dsign) + if (dsign) break; /* Must use bigcomp(). */ /* Here rv overestimates the truncated decimal value by at most @@ -1908,7 +1880,7 @@ _Py_dg_strtod(const char *s00, char **se) rv / 2^bc.scale >= 2^-1021. */ if (j - bc.scale >= 2) { dval(&rv) -= 0.5 * sulp(&rv, &bc); - break; + break; /* Use bigcomp. */ } } @@ -1922,7 +1894,7 @@ _Py_dg_strtod(const char *s00, char **se) /* Error is less than half an ulp -- check for * special case of mantissa a power of two. */ - if (bc.dsign || word1(&rv) || word0(&rv) & Bndry_mask + if (dsign || word1(&rv) || word0(&rv) & Bndry_mask || (word0(&rv) & Exp_mask) <= (2*P+1)*Exp_msk1 ) { break; @@ -1945,7 +1917,7 @@ _Py_dg_strtod(const char *s00, char **se) } if (i == 0) { /* exactly half-way between */ - if (bc.dsign) { + if (dsign) { if ((word0(&rv) & Bndry_mask1) == Bndry_mask1 && word1(&rv) == ( (bc.scale && @@ -1957,7 +1929,7 @@ _Py_dg_strtod(const char *s00, char **se) + Exp_msk1 ; word1(&rv) = 0; - bc.dsign = 0; + dsign = 0; break; } } @@ -1972,7 +1944,7 @@ _Py_dg_strtod(const char *s00, char **se) /* accept rv */ break; /* rv = smallest denormal */ - if (bc.nd >nd) + if (bc.nd > nd) break; goto undfl; } @@ -1984,7 +1956,7 @@ _Py_dg_strtod(const char *s00, char **se) } if (!(word1(&rv) & LSB)) break; - if (bc.dsign) + if (dsign) dval(&rv) += ulp(&rv); else { dval(&rv) -= ulp(&rv); @@ -1994,11 +1966,11 @@ _Py_dg_strtod(const char *s00, char **se) goto undfl; } } - bc.dsign = 1 - bc.dsign; + dsign = 1 - dsign; break; } if ((aadj = ratio(delta, bs)) <= 2.) { - if (bc.dsign) + if (dsign) aadj = aadj1 = 1.; else if (word1(&rv) || word0(&rv) & Bndry_mask) { if (word1(&rv) == Tiny1 && !word0(&rv)) { @@ -2022,7 +1994,7 @@ _Py_dg_strtod(const char *s00, char **se) } else { aadj *= 0.5; - aadj1 = bc.dsign ? aadj : -aadj; + aadj1 = dsign ? aadj : -aadj; if (Flt_Rounds == 0) aadj1 += 0.5; } @@ -2058,7 +2030,7 @@ _Py_dg_strtod(const char *s00, char **se) if ((z = (ULong)aadj) <= 0) z = 1; aadj = z; - aadj1 = bc.dsign ? aadj : -aadj; + aadj1 = dsign ? aadj : -aadj; } dval(&aadj2) = aadj1; word0(&aadj2) += (2*P+1)*Exp_msk1 - y; @@ -2075,7 +2047,7 @@ _Py_dg_strtod(const char *s00, char **se) L = (Long)aadj; aadj -= L; /* The tolerances below are conservative. */ - if (bc.dsign || word1(&rv) || word0(&rv) & Bndry_mask) { + if (dsign || word1(&rv) || word0(&rv) & Bndry_mask) { if (aadj < .4999999 || aadj > .5000001) break; } @@ -2104,20 +2076,28 @@ _Py_dg_strtod(const char *s00, char **se) word0(&rv0) = Exp_1 - 2*P*Exp_msk1; word1(&rv0) = 0; dval(&rv) *= dval(&rv0); - /* try to avoid the bug of testing an 8087 register value */ - if (!(word0(&rv) & Exp_mask)) - errno = ERANGE; } + ret: - if (se) - *se = (char *)s; return sign ? -dval(&rv) : dval(&rv); + parse_error: + return 0.0; + failed_malloc: - if (se) - *se = (char *)s00; errno = ENOMEM; return -1.0; + + undfl: + return sign ? -0.0 : 0.0; + + ovfl: + errno = ERANGE; + /* Can't trust HUGE_VAL */ + word0(&rv) = Exp_mask; + word1(&rv) = 0; + return sign ? -dval(&rv) : dval(&rv); + } static char * |