summaryrefslogtreecommitdiffstats
path: root/Python
diff options
context:
space:
mode:
authorMark Dickinson <dickinsm@gmail.com>2010-01-14 14:40:20 (GMT)
committerMark Dickinson <dickinsm@gmail.com>2010-01-14 14:40:20 (GMT)
commitf8747c1f12577a4198d47f28eaf89a2983a13261 (patch)
treecdb584b89986d028db208b6efa132bbf19669be1 /Python
parent50b60c612e7eca3f815d362190ddb80a21a6d820 (diff)
downloadcpython-f8747c1f12577a4198d47f28eaf89a2983a13261.zip
cpython-f8747c1f12577a4198d47f28eaf89a2983a13261.tar.gz
cpython-f8747c1f12577a4198d47f28eaf89a2983a13261.tar.bz2
Issue 7632: fix incorrect rounding for long input strings with values very close to a power of 2. (See Bug 4 in the tracker discussion.)
Diffstat (limited to 'Python')
-rw-r--r--Python/dtoa.c24
1 files changed, 24 insertions, 0 deletions
diff --git a/Python/dtoa.c b/Python/dtoa.c
index 5637c6c..51895c7 100644
--- a/Python/dtoa.c
+++ b/Python/dtoa.c
@@ -1738,6 +1738,30 @@ _Py_dg_strtod(const char *s00, char **se)
if (bc.nd > nd && i <= 0) {
if (bc.dsign)
break; /* Must use bigcomp(). */
+
+ /* Here rv overestimates the truncated decimal value by at most
+ 0.5 ulp(rv). Hence rv either overestimates the true decimal
+ value by <= 0.5 ulp(rv), or underestimates it by some small
+ amount (< 0.1 ulp(rv)); either way, rv is within 0.5 ulps of
+ the true decimal value, so it's possible to exit.
+
+ Exception: if scaled rv is a normal exact power of 2, but not
+ DBL_MIN, then rv - 0.5 ulp(rv) takes us all the way down to the
+ next double, so the correctly rounded result is either rv - 0.5
+ ulp(rv) or rv; in this case, use bigcomp to distinguish. */
+
+ if (!word1(&rv) && !(word0(&rv) & Bndry_mask)) {
+ /* rv can't be 0, since it's an overestimate for some
+ nonzero value. So rv is a normal power of 2. */
+ j = (int)(word0(&rv) & Exp_mask) >> Exp_shift;
+ /* rv / 2^bc.scale = 2^(j - 1023 - bc.scale); use bigcomp if
+ rv / 2^bc.scale >= 2^-1021. */
+ if (j - bc.scale >= 2) {
+ dval(&rv) -= 0.5 * sulp(&rv, &bc);
+ break;
+ }
+ }
+
{
bc.nd = nd;
i = -1; /* Discarded digits make delta smaller. */