On Fri, Jan 16, 2015 at 11:09:16PM -0500, Terry Reedy wrote:
On 1/12/2015 12:02 PM, Chris Barker wrote:
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.
That does nothing to solve problems A) and B), and I'm dubious that it provides a "significant figures comparison" (whatever that means, I'm pretty sure it doesn't mean "relative error", which is what you're calculating in a round-about fashion).
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
Adding the error tolerances together is a dubious thing to do. I don't understand the reasoning between that. Given two values a and b, there are two definitions of the error between them: absolute = abs(a - b) relative = abs(a - b)/abs(b) [Note: I'm sticking to numpy's unconditional use of "b" for the denominator, which is not symmetric. In actuality, I would use min(abs(a), abs(b)) for the denominator.] In the case where we only specify absolute or relative tolerance, it is obvious what to do: calculate the appropriate error, and if it is less than the given tolerance, return True: def approx_equal(a, b, allowed_absolute_error, allowed_relative_error): # For simplicity, ignore NANs, INFs, and assume b != 0 actual_error = abs(a - b) if allowed_absolute_error is None: # Only perform a check on relative error. return actual_error <= allowed_relative_error*abs(b) elif allowed_relative_error is None: # Only perform a check on absolute error. return actual_error <= allowed_absolute_error else: # We have specified *both* abs and rel error. How should we handle the third case? Two obvious ways come to mind: require that *both* individual tests pass: return (actual_error <= allowed_relative_error*abs(b) and actual_error <= allowed_absolute_error) # equivalent to: # actual_error <= max(allowed_relative_error*abs(b), # allowed_absolute_error) or require that *either* test pass: return (actual_relative_error <= allowed_relative_error or actual_absolute_error <= allowed_absolute_error) # equivalent to: # actual_error <= min( ... ) But what numpy does is to add the tolerances together, that is, it uses *twice* the average of them, equivalent to this: allowed_error = ( allowed_absolute_error + allowed_relative_error*abs(b) ) return actual_absolute_error <= allowed_error This means that numpy will claim that two numbers are close even though *both* the absolute and relative error tests fail: py> numpy.allclose([1.2], [1.0], 0.0, 0.1) # Fails absolute error test. False py> numpy.allclose([1.2], [1.0], 0.1, 0.0) # Fails relative error test. False py> numpy.allclose([1.2], [1.0], 0.1, 0.1) # Passes! True I cannot think of a good justification for that. Either I am missing something, or this goes to show that numpy can mess up even something as simple and straightforward as an error calculation. If I'm right, that's further evidence that getting this "right" and putting it in the standard library is a good thing to do. [...]
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.)
It isn't nonsensical, it just needs to be understood in context of relative errors. All non-zero numbers are infinitely far from zero in terms of relative error.
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. [snip anecdote]
I didn't think that the well-known difficulties in root-finding has anything to do with the usefulness of a standard way to compare numbers for approximate equality. -- Steven