[Tutor] Float precision untrustworthy~~~
C Smith
smichr at hotmail.com
Tue Mar 29 11:11:52 CEST 2005
On Monday, Mar 28, 2005, at 22:11 America/Chicago,
tutor-request at python.org wrote:
[cut]
> the mere fact that floats are difficult to check with equality has
> bitten me more than anything I've met yet in python.
>
[cut]
I understand what you are talking about, but I tend toward just making
it one of the things to remember when working with floats. (I've been
bitten a lot when I forget to use '==' instead of '=', too!)
As you say, the problems arise when you try to make comparisons. To get
around the difficulties with comparisons, I wrote a helper function:
###
Python 2.3 (#2, Jul 30 2003, 11:45:28)
[GCC 3.1 20020420 (prerelease)]
Type "copyright", "credits" or "license" for more information.
MacPython IDE 1.0.1
>>> from math import floor, log10
>>> def Round(x, n=0, sigfigs=False):
'''An enhanced rounder which rounds to the nth significant digit
(the nth digit from the first non-zero digit, not the nth decimal
place)
if the parameter sigfigs is not zero.
This uses "round-up" decisions when the digit following the one of
interest is a 5, i.e. 1.35 and 1.45 Round to 1.4 and 1.5 when
sigfigs = 2.
'''
if x==0: return 0
if not sigfigs:
return round(x,n)
else:
return round(x,n-1-int(floor(log10(abs(x)))))
###
And here's a helper that uses it to check for equality of two numbers
(floats, or otherwise)
to whatever number of digits you desire --starting from the first
non-zero digit, The default is for high precision.
###
>>> def same(x,y,prec=9):
"""Return True if x and y are the same after being rounded to the same
degree of precision."""
return Round(x,prec,1)==Round(y,prec,1)
..
>>> print same(1.3,1.31,3) #at the 3rd digit these are different
False
>>> print same(1.3,1.31,2) #at the second digit they are (rounded to)
the same digit number
True
>>> same(64.0**(1/3.0) ,4) #here's your example
True
###
You can be bitten by any comparison, not only tests for equality, too.
###
>>> while 1:
.. i+=.1
.. if i>2.1:break
..
>>> print i
2.1
###
It doesn't look like the last value of i was really greater than
2.1--it's actually 2.1000000000000005 so the result is correct (though
not what we expected). We don't have the problem with a step size of
0.7 in this case, though, because the value before 2.8 was
2.0999999999999996--just a little smaller than 2.1.
Here, we specify the precision and get the desired result.
###
>>> i=0
>>> while 1:
.. i+=.1
.. if round(i,9)>round(2.1,9):break
..
>>> print i
2.2
###
[cut]
> The last three checks also show that you need to define the places
> argument
> to get a good answer. Why not just implement decimal or some
> equivalent and
> get rid of hidden, hard to debug headaches? I understand uses of
> floats
> where the precision isn't exact anyway -- i.e. sqrt(2)
Wish granted in version 2.4 ;-) I don't have it on the mac, but there
is a write-up at
http://www.python.org/doc/2.4/whatsnew/node9.html
The starting blurb says:
"Python has always supported floating-point (FP) numbers, based on the
underlying C double type, as a data type. However, while most
programming languages provide a floating-point type, many people (even
programmers) are unaware that floating-point numbers don't represent
certain decimal fractions accurately. The new Decimal type can
represent these fractions accurately, up to a user-specified precision
limit. "
/c
More information about the Tutor
mailing list