[Tutor] range function and floats?

Dave Angel davea at ieee.org
Wed Jan 5 23:08:21 CET 2011


On 01/-10/-28163 02:59 PM, Wayne Werner wrote:
> On Wed, Jan 5, 2011 at 10:43 AM, Steven D'Aprano<steve at pearwood.info>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
>



More information about the Tutor mailing list