[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