[Python-ideas] Way to check for floating point "closeness"?

Terry Reedy tjreedy at udel.edu
Sat Jan 17 05:09:16 CET 2015


On 1/12/2015 12:02 PM, Chris Barker wrote:
> Now that we're talking about floating point conveniences (math.nan,
> linspace):
>
> What about putting an
>
> almost_equal(x,y,tol=1e14)
>
> (or close(), or...) in the math module.
>
> AFAICT, in the standard library, there is only:
>
> unittest.TestCase.assertAlmostEqual

As near as I can tell,
assertAlmostEqual(first, second, places=7, msg=None, delta=None)
does one of two possible absolute difference checks
(round(first, places) - round(second, places)) == 0.0
abs(first - second) < = delta

There has been discussion of making it more complicated, but I remember 
that being rejected because other tests require more thought and can be 
implemented with the above anyway.

> but it:
>
> A) is buried in the unittest.TestCase class
>
> B) is an assertion, so you can't use it as a general test (easily)
>
> C) uses number of decimal digits or an absolute delta, but does not
> provide a significant figures comparison, which is most likely what's
> wanted (and a bit harder to write yourself)

assertAlmostEqual((a-b)/d, 0, delta = tol)
where d is a, b, and (a+b)/2 as one thinks is appropriate.

> numpy provides allclose()

According to Neil Girdhar,
absolute(/a/ - /b/) <= (/atol/ + /rtol/ * absolute(/b/))
which I presume means, in Python,
abs(a-b) <= atol + rtol * abs(b)
where atol and rtol are assume >= 0.0

 > (and isclose() ), which is pretty much what I'm suggesting.
>
> Anyone else think this would be a good idea to add to the stdlib?

I am somewhat skeptical as there is no universal right answer (see 
below).  But I think allclose is about as good as we could get, maybe 
with some special casing added.

The discussion on the thread seems mostly divorced from the multiple use 
cases.  What do each of a and b represent? Different numbers?  or 
successive approximations of the same number?  Are they theoretical 
'exact' numbers, approximations of theoretical numbers, or calculations 
from measurements with error?  If the latter, how big is the error?  And 
why are we asking anyway?  We are usually asking 'close enough' for some 
purpose, but purposes differ.

Consider the problem of finding the (unique) 0 crossing (root) of a 
monotonic function f.  One is looking for a value x* such that f(x*) is 
'near' 0.  (Someone claimed that 'nothing is close to zero'.  This is 
nonsensical both in applied math and everyday life.)  A standard 
approach is to compute successive approximations until one finds such a 
point.  But unthinking application of such a method may not get one the 
desired result.  The following are two examples with opposite problems.

I once had the job of writing code to find the root of a monotonic 
function whose parameters were calculated from experimental data 
(multiple counts).  Fortunately, it was known that the unique root (a 
probability) had to be between 0.0 and 1.0 (endpoints excluded).  The 
function was often kinked (L-shaped) so that the slope at the root could 
be arbitrarily large.  That meant that for a small enough absolute 
tolerance, there might be no float x such that x would be even 'near' 0, 
let alone equal to 0.  And at other values, the slope could be so close 
to 0 as to make standard Newton iteration useless.  I ended up avoiding 
float comparison by simply doing 20 steps of binary search, starting 
with 0 and 1 as the initial 'approximations', and stopping.  The 
resulting 7 or 8 decimal digit approximation was more than good enough 
for realistic data.

On the other hand, consider f(x) = x**9,  Because f is so flat at its 
root (0) f(float) evaluates to -+0.0 for floats in a range of at least 
-1e-36, 1e-36.  Is any number in this range 'good enough' as the root? 
Or is more work needed to find a value near the middle of the '= 0' range?

-- 
Terry Jan Reedy



More information about the Python-ideas mailing list