Skip to content

Commit 45b6365

Browse files
committed
Merged revisions 77519,77530,77533 via svnmerge from
svn+ssh://pythondev@svn.python.org/python/trunk ........ r77519 | mark.dickinson | 2010-01-16 10:44:00 +0000 (Sat, 16 Jan 2010) | 5 lines Issue #7632: Fix a serious wrong output bug for string -> float conversion. Also remove some now unused variables, and add comments clarifying the possible outputs of the parsing section of _Py_dg_strtod. Thanks Eric Smith for reviewing. ........ r77530 | mark.dickinson | 2010-01-16 17:57:49 +0000 (Sat, 16 Jan 2010) | 3 lines Issue #7632: Fix one more case of incorrect rounding for str -> float conversion (see bug 5 in the issue tracker). ........ r77533 | mark.dickinson | 2010-01-16 18:06:17 +0000 (Sat, 16 Jan 2010) | 1 line Fix multiple uses of variable 'L' in _Py_dg_strtod, where one use requires an unsigned long and the other a signed long. See also r77421. ........
1 parent 747e8b3 commit 45b6365

File tree

3 files changed

+100
-47
lines changed

3 files changed

+100
-47
lines changed

Lib/test/test_strtod.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -123,10 +123,6 @@ def test_halfway_cases(self):
123123
digits = m * 5**-e
124124
exponent = e
125125
s = '{}e{}'.format(digits, exponent)
126-
127-
# for the moment, ignore errors from trailing zeros
128-
if digits % 10 == 0:
129-
continue
130126
self.check_strtod(s)
131127

132128
# get expected answer via struct, to triple check
@@ -175,7 +171,8 @@ def test_bigcomp(self):
175171
self.check_strtod(s)
176172

177173
def test_parsing(self):
178-
digits = tuple(map(str, range(10)))
174+
# make '0' more likely to be chosen than other digits
175+
digits = '000000123456789'
179176
signs = ('+', '-', '')
180177

181178
# put together random short valid strings
@@ -257,7 +254,9 @@ def test_particular(self):
257254
'247032822920623295e-341',
258255
# issue 7632 bug 5: the following 2 strings convert differently
259256
'1000000000000000000000000000000000000000e-16',
260-
#'10000000000000000000000000000000000000000e-17',
257+
'10000000000000000000000000000000000000000e-17',
258+
# issue 7632 bug 8: the following produced 10.0
259+
'10.900000000000000012345678912345678912345',
261260
]
262261
for s in test_strings:
263262
self.check_strtod(s)

Misc/NEWS

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,12 @@ Core and Builtins
1616
methods of bytes, bytearray and unicode objects by using a common
1717
implementation based on stringlib's fast search. Patch by Florent Xicluna.
1818

19-
- Issue #7632: Fix a crash in dtoa.c that occurred in debug builds
20-
when parsing certain long numeric strings corresponding to subnormal
21-
values. Also fix a number of bugs in dtoa.c that could lead to
22-
incorrectly rounded results when converting strings to floats.
19+
- Issue #7632: Fix various str -> float conversion bugs present in 2.7
20+
alpha 2, including: (1) a serious 'wrong output' bug that could
21+
occur for long (> 40 digit) input strings, (2) a crash in dtoa.c
22+
that occurred in debug builds when parsing certain long numeric
23+
strings corresponding to subnormal values, and (3) a number of flaws
24+
that could lead to incorrectly rounded results.
2325

2426
- The __complex__ method is now looked up on the class of instances to make it
2527
consistent with other special methods.

Python/dtoa.c

Lines changed: 89 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1340,16 +1340,17 @@ bigcomp(U *rv, const char *s0, BCinfo *bc)
13401340
double
13411341
_Py_dg_strtod(const char *s00, char **se)
13421342
{
1343-
int bb2, bb5, bbe, bd2, bd5, bbbits, bs2, c, dp0, dp1, dplen, e, e1, error;
1343+
int bb2, bb5, bbe, bd2, bd5, bbbits, bs2, c, e, e1, error;
13441344
int esign, i, j, k, nd, nd0, nf, nz, nz0, sign;
13451345
const char *s, *s0, *s1;
13461346
double aadj, aadj1;
13471347
U aadj2, adj, rv, rv0;
1348-
ULong y, z, L;
1348+
ULong y, z, abse;
1349+
Long L;
13491350
BCinfo bc;
13501351
Bigint *bb, *bb1, *bd, *bd0, *bs, *delta;
13511352

1352-
sign = nz0 = nz = dplen = 0;
1353+
sign = nz0 = nz = 0;
13531354
dval(&rv) = 0.;
13541355
for(s = s00;;s++) switch(*s) {
13551356
case '-':
@@ -1381,18 +1382,11 @@ _Py_dg_strtod(const char *s00, char **se)
13811382
goto ret;
13821383
}
13831384
s0 = s;
1384-
y = z = 0;
13851385
for(nd = nf = 0; (c = *s) >= '0' && c <= '9'; nd++, s++)
1386-
if (nd < 9)
1387-
y = 10*y + c - '0';
1388-
else if (nd < 16)
1389-
z = 10*z + c - '0';
1386+
;
13901387
nd0 = nd;
1391-
dp0 = dp1 = s - s0;
13921388
if (c == '.') {
13931389
c = *++s;
1394-
dp1 = s - s0;
1395-
dplen = 1;
13961390
if (!nd) {
13971391
for(; c == '0'; c = *++s)
13981392
nz++;
@@ -1409,15 +1403,7 @@ _Py_dg_strtod(const char *s00, char **se)
14091403
nz++;
14101404
if (c -= '0') {
14111405
nf += nz;
1412-
for(i = 1; i < nz; i++)
1413-
if (nd++ < 9)
1414-
y *= 10;
1415-
else if (nd <= DBL_DIG + 1)
1416-
z *= 10;
1417-
if (nd++ < 9)
1418-
y = 10*y + c;
1419-
else if (nd <= DBL_DIG + 1)
1420-
z = 10*z + c;
1406+
nd += nz;
14211407
nz = 0;
14221408
}
14231409
}
@@ -1440,17 +1426,17 @@ _Py_dg_strtod(const char *s00, char **se)
14401426
while(c == '0')
14411427
c = *++s;
14421428
if (c > '0' && c <= '9') {
1443-
L = c - '0';
1429+
abse = c - '0';
14441430
s1 = s;
14451431
while((c = *++s) >= '0' && c <= '9')
1446-
L = 10*L + c - '0';
1447-
if (s - s1 > 8 || L > MAX_ABS_EXP)
1432+
abse = 10*abse + c - '0';
1433+
if (s - s1 > 8 || abse > MAX_ABS_EXP)
14481434
/* Avoid confusion from exponents
14491435
* so large that e might overflow.
14501436
*/
14511437
e = (int)MAX_ABS_EXP; /* safe for 16 bit ints */
14521438
else
1453-
e = (int)L;
1439+
e = (int)abse;
14541440
if (esign)
14551441
e = -e;
14561442
}
@@ -1468,15 +1454,78 @@ _Py_dg_strtod(const char *s00, char **se)
14681454
}
14691455
goto ret;
14701456
}
1471-
bc.e0 = e1 = e -= nf;
1457+
e -= nf;
1458+
if (!nd0)
1459+
nd0 = nd;
1460+
1461+
/* strip trailing zeros */
1462+
for (i = nd; i > 0; ) {
1463+
/* scan back until we hit a nonzero digit. significant digit 'i'
1464+
is s0[i] if i < nd0, s0[i+1] if i >= nd0. */
1465+
--i;
1466+
if (s0[i < nd0 ? i : i+1] != '0') {
1467+
++i;
1468+
break;
1469+
}
1470+
}
1471+
e += nd - i;
1472+
nd = i;
1473+
if (nd0 > nd)
1474+
nd0 = nd;
14721475

14731476
/* Now we have nd0 digits, starting at s0, followed by a
14741477
* decimal point, followed by nd-nd0 digits. The number we're
14751478
* after is the integer represented by those digits times
14761479
* 10**e */
14771480

1478-
if (!nd0)
1479-
nd0 = nd;
1481+
bc.e0 = e1 = e;
1482+
1483+
/* Summary of parsing results. The parsing stage gives values
1484+
* s0, nd0, nd, e, sign, where:
1485+
*
1486+
* - s0 points to the first significant digit of the input string s00;
1487+
*
1488+
* - nd is the total number of significant digits (here, and
1489+
* below, 'significant digits' means the set of digits of the
1490+
* significand of the input that remain after ignoring leading
1491+
* and trailing zeros.
1492+
*
1493+
* - nd0 indicates the position of the decimal point (if
1494+
* present): so the nd significant digits are in s0[0:nd0] and
1495+
* s0[nd0+1:nd+1] using the usual Python half-open slice
1496+
* notation. (If nd0 < nd, then s0[nd0] necessarily contains
1497+
* a '.' character; if nd0 == nd, then it could be anything.)
1498+
*
1499+
* - e is the adjusted exponent: the absolute value of the number
1500+
* represented by the original input string is n * 10**e, where
1501+
* n is the integer represented by the concatenation of
1502+
* s0[0:nd0] and s0[nd0+1:nd+1]
1503+
*
1504+
* - sign gives the sign of the input: 1 for negative, 0 for positive
1505+
*
1506+
* - the first and last significant digits are nonzero
1507+
*/
1508+
1509+
/* put first DBL_DIG+1 digits into integer y and z.
1510+
*
1511+
* - y contains the value represented by the first min(9, nd)
1512+
* significant digits
1513+
*
1514+
* - if nd > 9, z contains the value represented by significant digits
1515+
* with indices in [9, min(16, nd)). So y * 10**(min(16, nd) - 9) + z
1516+
* gives the value represented by the first min(16, nd) sig. digits.
1517+
*/
1518+
1519+
y = z = 0;
1520+
for (i = 0; i < nd; i++) {
1521+
if (i < 9)
1522+
y = 10*y + s0[i < nd0 ? i : i+1] - '0';
1523+
else if (i < DBL_DIG+1)
1524+
z = 10*z + s0[i < nd0 ? i : i+1] - '0';
1525+
else
1526+
break;
1527+
}
1528+
14801529
k = nd < DBL_DIG + 1 ? nd : DBL_DIG + 1;
14811530
dval(&rv) = y;
14821531
if (k > 9) {
@@ -1593,15 +1642,18 @@ _Py_dg_strtod(const char *s00, char **se)
15931642
/* ASSERT(STRTOD_DIGLIM >= 18); 18 == one more than the */
15941643
/* minimum number of decimal digits to distinguish double values */
15951644
/* in IEEE arithmetic. */
1596-
i = j = 18;
1597-
if (i > nd0)
1598-
j += dplen;
1599-
for(;;) {
1600-
if (--j <= dp1 && j >= dp0)
1601-
j = dp0 - 1;
1602-
if (s0[j] != '0')
1603-
break;
1645+
1646+
/* Truncate input to 18 significant digits, then discard any trailing
1647+
zeros on the result by updating nd, nd0, e and y suitably. (There's
1648+
no need to update z; it's not reused beyond this point.) */
1649+
for (i = 18; i > 0; ) {
1650+
/* scan back until we hit a nonzero digit. significant digit 'i'
1651+
is s0[i] if i < nd0, s0[i+1] if i >= nd0. */
16041652
--i;
1653+
if (s0[i < nd0 ? i : i+1] != '0') {
1654+
++i;
1655+
break;
1656+
}
16051657
}
16061658
e += nd - i;
16071659
nd = i;
@@ -1611,8 +1663,8 @@ _Py_dg_strtod(const char *s00, char **se)
16111663
y = 0;
16121664
for(i = 0; i < nd0; ++i)
16131665
y = 10*y + s0[i] - '0';
1614-
for(j = dp1; i < nd; ++i)
1615-
y = 10*y + s0[j++] - '0';
1666+
for(; i < nd; ++i)
1667+
y = 10*y + s0[i+1] - '0';
16161668
}
16171669
}
16181670
bd0 = s2b(s0, nd0, nd, y);

0 commit comments

Comments
 (0)