[Tutor] Python 2.5.4 - error in rounding
Steven D'Aprano
steve at pearwood.info
Sat May 22 18:58:57 CEST 2010
On Sun, 23 May 2010 12:19:07 am Wayne Werner wrote:
> On Sat, May 22, 2010 at 7:32 AM, Steven D'Aprano
<steve at pearwood.info>wrote:
> > Why do people keep recommending Decimal? Decimals suffer from the
> > exact same issues as floats,
>
> This is exactly incorrect! The Decimal operator offers /exact/
> decimal point operations.
Decimal is only exact for fractions which can be represented by a finite
sum of powers-of-ten, like 0.1, just like floats can only represent
fractions exactly if they can be represented by a finite sum of
powers-of-two, like 0.5.
Not only did I demonstrate an example of rounding error using Decimal in
my post, but you then repeated that rounding error and then had the
audacity to claim that it was "exact":
> For an example about the exactness of Decimal v Float:
> >>> d = Decimal(1)/Decimal(3)
> >>> d
>
> Decimal('0.3333333333333333333333333333')
>
> >>> d*Decimal(3)
>
> Decimal('0.9999999999999999999999999999')
Does that look like exactly one to you? I don't know what they taught
you, but when I was in school, I learned that one was exactly 1, not
0.9 or 0.999 or even 0.9999999999999999999999999999.
But don't believe five hundred years of mathematics, do the test
yourself:
>>> d*Decimal(3) == 1
False
We can calculate the exact error:
>>> d = Decimal(1)/Decimal(3)
>>> 1 - d*Decimal(3) == 0
False
>>> 1 - d*Decimal(3)
Decimal('1E-28')
Small, but not zero. And adding more precision isn't the answer: it will
make the error smaller, true enough, but not zero:
>>> decimal.getcontext().prec = 100
>>> d = Decimal(1)/Decimal(3)
>>> d*Decimal(3) == 1
False
>>> 1 - d*Decimal(3)
Decimal('1E-100')
To get that error to zero exactly, you need an infinite precision.
> They implement non-hardware operations to
> preserve exactness. For more information on exactly what and how the
> decimal module does what it does, see the following:
>
> http://docs.python.org/library/decimal.html
I know what the decimal module does. It is capable of representing
*decimal* numbers exactly, but not all fractions are exact decimal
numbers, just as not all fractions are exact binary numbers. For exact
fractions, you need the fractions module.
> plus they are slower.
>
>
> Because if memory serves correctly the Python implementation uses
> serial arithmetic, rather than the hardware implementation of
> floating point calculations.
I don't understand what you mean by serial arithmetic, but the reason
the decimal module is slow is that it is currently written in pure
Python. A C version would be faster (but still slower than the hardware
implementation of float calculations).
> Please stop propagating myths about the Decimal module.
I'm not. You are misrepresenting Decimal as a panacea for all rounding
issues, which it is not.
> >>> d = 1/3.0
> >>> d*3
> 1.0
Curiously, floats perform that specific calculation better than Decimal.
That shows that sometimes you can have *too much* precision for a
calculation. float rounds off the answer after just 17 decimal places
(by memory), giving exactly 1, while Decimal (by default) rounds to 28
places, leading to an error.
Of course, there are other calculations where Decimal is more accurate:
>>> sum(0.1 for i in range(10)) == 1
False
>>> sum(Decimal('0.1') for i in range(10)) == 1
True
This is only to be expected, because 0.1 is an exact power of ten, but
not an exact power of two.
--
Steven D'Aprano
More information about the Tutor
mailing list