[PythonDev] 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.e5, absolute_err=1.e8):
 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.e8, rel_tol=1.e5):
'''Return True if xy < abs_tol or xy/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=1e5)
close(x,y,abs_tol=1e8)
/c
More information about the PythonDev
mailing list