[Tutor] Decimals 'not equal to themselves' (e.g. 0.2 equals 0.200000001)

Terry Carroll carroll at tjc.com
Wed Aug 6 03:29:37 CEST 2008


On Sun, 3 Aug 2008, CNiall wrote:

>  >>> 0.2
> 0.20000000000000001
>  >>> 0.33
> 0.33000000000000002
> 
> As you can see, the last two decimals are very slightly inaccurate. 
> However, it appears that when n in 1/n is a power of two, the decimal 
> does not get 'thrown off'. How might I make Python recognise 0.2 as 0.2 
> and not 0.20000000000000001?

It's not a Python thing, it's a computer thing.  Thsi would be present 
regardless of what computer language you're using.

An oversimplificationis that computers use binary, i.e., base-2.  The only 
non-1 factor of 2 is 2 itself, and computers can only give you an exact 
representation of a fraction whose denominator only has factors of 2.

So these are exact representations:

>>> .5
0.5
>>> .5     # 1/2
0.5
>>> .25    # 1/4
0.25
>>> .125   # 1/8
0.125
>>> .875   # 7/8
0.875

But suppose you want 1/10.  Well, 10 is factored into 2 and 5, and that 
pesky 5 makes it impossible for a computer to store exactly; same with 
1/5:

>>> .1     # 1/10
0.10000000000000001
>>> .2     # 1/5
0.20000000000000001

Do you remember when you learned decimal fractions in garde school, and 
realized you couldn't represent, for example, 1/3 or 1/7 exactly?  You had 
to content yourself with 0.3333333.... or 0.14285714285714285... with 
digits repeating forever?  

That's the equivalent problem in our usual base-10: we can't exactly
represent any fraction unless the denominator factors only into 2s and 5s 
(which are the factors of 10).

So representing 1/2, 1/4, 1/20 all are no problem in decimal; but 1/3 and 
1/21 can't be exactly represented.

A corallary of this, by the way, is that, because there are so many 
fractions that can't be exactly represented in binary notation, you should 
never compare floating point numbers looking for equality.  It just won't 
work.

Consider the following code:

>>> x = 0.0
>>> while x != 1.0:
...   print x
...   x = x + 1.0/7.0

You might expect this to look through 0.0, 0.142...., 0.285..., 0.428....
up to 10., and then stop. But it won't.  because it never quite equals
1.0.  It goes right up to a number near 1.0, that might display as 1.0,
but is not really 1.0, and blasts on through to 1.142... etc, in an
endless loop.

So when comparing floating point numbers you should either use a 
comparison like >= (if using it as a limit) or a construct like

  if abs(x-y)<.00001:

to see if they're close enough to equal to keep you happy.




More information about the Tutor mailing list