[Python-ideas] PEP 485: A Function for testing approximate equality

Steven D'Aprano steve at pearwood.info
Sun Jan 25 14:03:27 CET 2015


On Sat, Jan 24, 2015 at 05:27:57PM +1000, Nick Coghlan wrote:

> I would personally find the PEP more persuasive if it was framed in
> terms of providing an improved definition of assertAlmostEqual that
> better handles the limitations of binary floating point dynamic
> ranges.
> 
> The fact that unittest.assertAlmostEqual is in the standard library
> implies that any improvement to it must also be in the standard
> library, and moving the definition of near equality that unittest uses
> out to the math module so it is reusable in other contexts makes sense
> to me, especially if it means being able to share the definition
> between unittest and the statistics module.

Unfortunately, I don't think we can change assertAlmostEqual. If we 
change the implementation, tests which were passing may fail, and tests 
which were failing may pass.

The approach I took for test_statistics was to subclass TestCase and add 
my own approx-equal assertion method:

https://hg.python.org/cpython/file/fcab9c106f2f/Lib/test/test_statistics.py#l142

In my tests, I call assertApproxEqual(first, second), with optional 
values tol (absolute error tolerance) and rel (relative error). If tol 
and/or rel are not used, the assertion defaults to self.tol or self.rel, 
which if not set default to 0, which is equivalent to testing exact 
equality.

The actual fuzzy comparison itself is handled by a function 
approx_equal(x, y, tol, rel).

I propose:

- somebody other than me should review NumericTestCase.assertApproxEqual 
  and check that it does nothing unreasonable;

- the assertApproxEqual method be moved into TestCase, making it 
  available to any user of unittest;

- the fuzzy comparison approx_equal can be turned into a static(?) 
  method of TestCase (see below for the advantage of a method rather 
  than a function);

- since there are considerable disagreements about the right way to 
  handle a fuzzy comparison when *both* an absolute and relative error 
  are given, people who disagree with the default definition can simply 
  subclass TestCase and redefine the approx_equal method.

(Which is much simpler than having to write the whole assertApproxEqual 
method from scratch.)


This gives a workable (although not obvious) solution to Guido's example 
use-case, which might become:

from unittest import TestCase
are_close = TestCase.approx_equal

def sqrt(x):
    new_guess = 1
    repeat:
        guess = new_guess
        new_guess = avg(guess, x/guess)
    until are_close(guess, new_guess)
    return guess


Note that in this case, at least, we do want a symmetric version of 
"is_close", since neither guess nor new_guess is "correct", they are 
both approximations.



-- 
Steven


More information about the Python-ideas mailing list