[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