[Python-Dev] math.areclose ...?

Smith smiles at worksmail.net
Wed Feb 8 06:57:18 CET 2006


Raymond Hettinger wrote:
| [Chris Smith]
|| Does it help to spell it like this?
|| 
|| def areclose(x, y, relative_err = 1.e-5, absolute_err=1.e-8):
||     diff = abs(x - y)
||     ave = (abs(x) + abs(y))/2
||     return diff < absolute_err or diff/ave < relative_err
| 
| There is a certain beauty and clarity to this presentation; however,
| it is problematic numerically:
| 
| * the division by either absolute_err and relative_err can overflow or
| trigger a ZeroDivisionError

I'm not dividing by either of these values so that shouldn't be a problem. As long as absolute_err is not 0 then the first test would catch the possiblity that x==y==ave==0. (see below)

As for the overflow, does your version of python overflow? Mine (2.4) just returns 1.#INF which still computes as a number:

###
>>> 1.79769313486e+308+1.79769313486e+308
1.#INF
>>> inf=_
>>> inf>1
True
>>> inf<1
False
>>> 2./inf
0.0
>>> inf/inf
-1.#IND
###


There is a problem with dividing by 'ave' if the x and y are at the floating point limits, but the symmetric behaving form (presented by Scott Daniels) will have the same problem. The following format for close() has the same semantic meaning but avoids the overflow possibility and avoids extra work for the case when abs_tol=0 and x==y:

###
def close(x, y, abs_tol=1.e-8, rel_tol=1.e-5):
 '''Return True if |x-y| < abs_tol or |x-y|/ave(|x|,|y|) < rel_tol.
 The average is not computed directly so as to avoid overflow for
 numbers close to the floating point upper limit.'''
 if x==y: return True
 diff = abs(x - y)
 if diff < abs_tol: return True
 f = rel_tol/2.
 if diff < f*abs(x) + f*abs(y): return True
 return False
###

| 
| * the 'or' part of the expression can introduce an unnecessary
| discontinuity in the first derivative.
|
If a value other than boolean were being returned, I could see the desire for continuity in derivative. Since the original form presents a boolean result, however, I'm having a hard time thinking of how the continuity issue comes to play.
 
| The original Numeric definition is likely to be better for people who
| know what they're doing; however, I still question whether it is an
| appropriate remedy for the beginner issue
| of why 1.1 + 1.1 + 1.1 doesn't equal 3.3.
|

I'm in total agreement. Being able to see that math.areclose(1.1*3,3.3) is True but 1.1*3==3.3 is False is not going to make them feel much better. They are going to have to face the floating point issue. 

As for the experienced user, perhaps such a function would be helpful. Maybe it would be better to require that the tolerances be given rather than defaulting so as to make clear which test is being used if only one test was going to be used:

close(x,y,rel_tol=1e-5)
close(x,y,abs_tol=1e-8)

/c


More information about the Python-Dev mailing list