
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#... 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