[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