On 01/-10/-28163 02:59 PM, Wayne Werner wrote:
On Wed, Jan 5, 2011 at 10:43 AM, Steven D'Aprano<[email protected]>wrote:
Wayne Werner wrote:
The decimal module allows you to get rid of those pesky floating point
errors. See http://docs.python.org/library/decimal.html for more info.
That's a myth. Decimal suffers from the same floating point issues as
binary floats. It's quite easy to demonstrate the same sort of rounding
errors with Decimal as for float:
from decimal import Decimal as D
x = D(1)/D(3)
3*x == 1
False
I should have clarified - when I say pesky floating point errors I mean
errors in precision that you naturally would not expect.
I'd expect them. Apparently you would not.
1/3 == .333 (repeating forever(?)). But on a computer, you're limited to a
specific precision point (sort of), and the moment you truncate
.333(repeating) to *any* finite points of precision you no longer have the
result of the mathematical operation 1/3. Yes, 1/3*3 == 1, but the error in
the Decimal module is *only* in division. It might be useful to define a
"repeating" flag in the Decimal module for the results of such operations as
1/3, which would get rid of the error in truncation.
Interesting flag. So how would it indicate that the decimal version of
the number 762/477 repeats every 476 digits?
But this is a
fundamentally different error from the standard floating point errors.
In [106]: 0.9
Out[106]: 0.90000000000000002
These "errors" are in the conversion between string and floating point,
and back again. The number 0.9 has a repeating bit pattern in binary
floating point, so it cannot be represented exactly. That's not an
error, it's a direct consequence of the decision to use binary floating
point. And that decision was made decades ago, and is independent of
Python. When I learned Fortran in the 60's, one of the things the book
(McCracken) emphasized was not to compare real numbers with equality,
but to use the form abs(x-y)<delta.
These are two different numbers, mathematically. If I say x = 0.9, I
naturally assume that 0.9 is the value in x, not 0.90000000000000002. Of
course it's all in the implementation of floating points, and the fact that
Python evaluates 0.9 in a floating point context which results in the stored
value, but that ignores the fact that *naturally* one does not expect this.
And anyone who's been through 2nd or 3rd grade or whenever they teach about
equality would expect that this would evaluate to False.
Lots of stuff they teach in 3rd grade is oversimplified for the real world.
In [112]: 0.90000000000000002 == 0.9
Out[112]: True
You don't get such silliness with the Decimal module:
In [125]: D('0.90000000000000002') == D('0.9')
Out[125]: False
The conversion between string and float is logically a divide, where the
denominator is a power of 10. So decimal floating point doesn't have
any quantization errors for that conversion.
Even if you use the decimal package, you need to understand these
things, or you'll make the kind of error made a few days ago, where
binary floating point values were passed into a decimal constructor.
That won't "fix" problems that were already there. It just may mask
them for certain cases.
Between 1 and 1000 inclusive, there are 354 such numbers:
nums = []
for i in range(1, 1001):
... x = D(1)/D(i)
... if x*i != 1: nums.append(i)
...
len(nums)
354
The problem isn't just division and multiplication, nor does it just affect
fractional numbers:
x = D(10)**30
x + 100 == x
True
Decimal DOES get rid of floating point errors, except in the case of
repeating (or prohibitively large precision values)
In [127]: x = D(10)**30
In [128]: x
Out[128]: Decimal('1.000000000000000000000000000E+30')
In [129]: x + 100
Out[129]: Decimal('1.000000000000000000000000000E+30')
If you reset the precision to an incredibly large number:
decimal.getcontext().prec = 1000
In [131]: x = D(10)**30
In [132]: x
Out[132]: Decimal('1000000000000000000000000000000')
In [133]: x + 100 == x
Out[133]: False
Voila, the error has vanished!
So it simply isn't correct to suggest that Decimal doesn't suffer from
rounding error.
I never said rounding errors - I said "pesky floating point errors". When
performing the operation 1/3, I naturally expect that my computer won't hold
each of the 3's after the decimal point, and I don't categorize that as
pesky - that's just good sense if you know a little about computers. I also
expect that .333 * 3 would give me the number .999, and only .999, not
.9990000000011 or some other wonky value. Of course it's interesting to note
that Python handles the precision properly when dealing with strings, but
not with the floating points themselves (at least on this particular trial):
In [141]: .333 * 3
Out[141]: 0.99900000000000011
In [142]: str(.333*3)
Out[142]: '0.999'
In [143]: .333 * 3 == .999
Out[143]: False
In [144]: str(.333*3) == str(.999)
Out[144]: True
Decimal and float share more things in common than differences. Both are
floating point numbers. Both have a fixed precision (although you can
configure what that precision is for Decimal, but not float). Both use a
finite number of bits, and therefore have a finite resolution. The only
differences are:
* as mentioned, you can configure Decimal to use more bits and higher
precision, at the cost of speed and memory;
* floats use base 2 and Decimal uses base 10;
* floats are done in hardware and so are much faster than Decimal;
Precisely. It's not a magic bullet (1/3 != .33333333 mathematically, after
all!), *but* it eliminates the errors that you wouldn't normally expect when
working with "standard" points of precision, such as the expectation that
0.333 * 3 resulting in .999, not .99900000000011.
Hopefully a little more precise,
Wayne
_______________________________________________
Tutor maillist - [email protected]
To unsubscribe or change subscription options:
http://mail.python.org/mailman/listinfo/tutor