diff -r 00c95b0e82e4 Doc/library/unittest.rst --- a/Doc/library/unittest.rst Thu Jun 02 11:36:16 2016 -0700 +++ b/Doc/library/unittest.rst Fri Jun 03 11:36:44 2016 -0700 @@ -1091,6 +1091,12 @@ | :meth:`assertNotAlmostEqual(a, b) | ``round(a-b, 7) != 0`` | | | ` | | | +---------------------------------------+--------------------------------+--------------+ + | :meth:`assertClose(a, b) | ``math.isclose(a, b)`` | 3.6 | + | ` | | | + +---------------------------------------+--------------------------------+--------------+ + | :meth:`assertNotClose(a, b) | ``not math.isclose(a, b)`` | 3.6 | + | ` | | | + +---------------------------------------+--------------------------------+--------------+ | :meth:`assertGreater(a, b) | ``a > b`` | 3.1 | | ` | | | +---------------------------------------+--------------------------------+--------------+ @@ -1122,7 +1128,8 @@ equal by computing the difference, rounding to the given number of decimal *places* (default 7), and comparing to zero. Note that these methods round the values to the given number of *decimal places* (i.e. - like the :func:`round` function) and not *significant digits*. + like the :func:`round` function) and not *significant digits*. If you + need something like significant digits, consider :meth:`assertClose` If *delta* is supplied instead of *places* then the difference between *first* and *second* must be less or equal to (or greater than) *delta*. @@ -1134,6 +1141,43 @@ that compare equal. :meth:`assertNotAlmostEqual` automatically fails if the objects compare equal. Added the *delta* keyword argument. + .. method:: assertClose(self, first, second, rel_tol=1e-09, abs_tol=0.0, msg=None) + assertNotClose(self, first, second, rel_tol=1e-09, abs_tol=0.0, msg=None) + + Tests if two floating point numbers are close to the same value as + determined by their difference relative to their magnitude. + This is computed by the :func:`math.isclose()` function. + + rel_tol=1e-09 + maximum difference for being considered close, relative to the + magnitude of the input values + abs_tol=0.0 + maximum difference for being considered close, regardless of the + magnitude of the input values. This is required if one of the values + is exactly zero, as nothing is "relatively" close to zero. + + For the values to be considered close, the difference between them + must be smaller than at least one of the tolerances. + + + Note that in contrast to :meth:`assertAlmostEqual`, this is similar to + significant digits (measured from the most significant digit). For + example, setting rel_tol=1e-9 tests equality of approximately nine + decimal digits (approximate, because the arithmetic is done in binary, + not decimal). :meth:`assertAlmostEqual` should be used when you know the + magnitude of your values, and you want them compared to given number + of decimal places. ``assertClose()`` can be used for any magnitude of + values. + + ``-inf``, ``inf`` and ``NaN`` behave similarly to the IEEE 754 Standard. That + is, NaN is not close to anything, even itself. ``inf`` and ``-inf`` are + only close to themselves. + + If the two objects compare equal then they will automatically + be considered close. + + .. versionadded:: 3.6 +.. :meth:`assertClose` and :meth:`assertNotClose` added to use the `math.isclose()` method. .. method:: assertGreater(first, second, msg=None) assertGreaterEqual(first, second, msg=None) diff -r 00c95b0e82e4 Lib/unittest/case.py --- a/Lib/unittest/case.py Thu Jun 02 11:36:16 2016 -0700 +++ b/Lib/unittest/case.py Fri Jun 03 11:36:44 2016 -0700 @@ -10,6 +10,7 @@ import collections import contextlib import traceback +import math from . import result from .util import (strclass, safe_repr, _count_diff_all_purpose, @@ -899,6 +900,93 @@ msg = self._formatMessage(msg, standardMsg) raise self.failureException(msg) + def assertClose(self, first, second, rel_tol=1e-09, abs_tol=0.0, msg=None): + """Fail if the two numbers are not close to the same value as + determined by their difference relative to their magnitude. + This is computed by the isclose() function in the math module. + + rel_tol=1e-09 + maximum difference for being considered "close", relative to the + magnitude of the input values + abs_tol=0.0 + maximum difference for being considered "close", regardless of the + magnitude of the input values. This is required if one of the values + is close to zero, as nothing is "relatively" close to zero. + + For the values to be considered close, the difference between them + must be smaller than at least one of the tolerances. + + -inf, inf and NaN behave similarly to the IEEE 754 Standard. That + is, NaN is not close to anything, even itself. inf and -inf are + only close to themselves. + + Note that in contrast to ``assertAlmostEqual``, this is similar to + significant digits (measured from the most significant digit). For + example, setting rel_tol=1e-9 tests equality of approximately nine + decimal digits. (close, because the arithmetic is done in binary, + not decimal) + + If the two objects compare equal then they will automatically + compare close. + """ + if first == second: + # shortcut + return + if math.isclose(first, second, rel_tol=rel_tol, abs_tol=abs_tol): + return + else: + standardMsg = ('%s != %s with relative tolerance of %.3g' + ' and absolute tolerance of %.3g' % (safe_repr(first), + safe_repr(second), + rel_tol, + abs_tol)) + msg = self._formatMessage(msg, standardMsg) + raise self.failureException(msg) + + def assertNotClose(self, first, second, rel_tol=1e-09, abs_tol=0.0, msg=None): + """Fail if the two numbers are close to the same value as + determined by their difference relative to their magnitude. + This is computed by the isclose() function in the math module. + + rel_tol=1e-09 + maximum difference for being considered "close", relative to the + magnitude of the input values + abs_tol=0.0 + maximum difference for being considered "close", regardless of the + magnitude of the input values. This is required if one of the values + is close to zero, as nothing is "relatively" close to zero. + + For the values to be considered close, the difference between them + must be smaller than at least one of the tolerances. + + -inf, inf and NaN behave similarly to the IEEE 754 Standard. That + is, NaN is not close to anything, even itself. inf and -inf are + only close to themselves. + + Note that in contrast to ``assertAlmostEqual``, this is similar to + significant digits (measured from the most significant digit). For + example, setting rel_tol=1e-9 tests equality of approximately nine + decimal digits. (close, because the arithmetic is done in binary, + not decimal) + + If the two objects compare equal then they will automatically + compare close. + """ + if first == second: + # shortcut + standardMsg = '%s != %s are exactly equal' % (safe_repr(first), + safe_repr(second), + ) + elif not math.isclose(first, second, rel_tol=rel_tol, abs_tol=abs_tol): + return + else: + standardMsg = ('%s == %s with relative tolerance of %.3g' + ' and absolute tolerance of %.3g' % (safe_repr(first), + safe_repr(second), + rel_tol, + abs_tol)) + msg = self._formatMessage(msg, standardMsg) + raise self.failureException(msg) def assertSequenceEqual(self, seq1, seq2, msg=None, seq_type=None): """An equality assertion for ordered sequences (like lists and tuples). diff -r 00c95b0e82e4 Lib/unittest/test/test_assertions.py --- a/Lib/unittest/test/test_assertions.py Thu Jun 02 11:36:16 2016 -0700 +++ b/Lib/unittest/test/test_assertions.py Fri Jun 03 11:36:44 2016 -0700 @@ -56,6 +56,28 @@ self.assertNotAlmostEqual(first, second, delta=datetime.timedelta(seconds=5)) + def test_assertClose(self): + # Note: math.isclose() has more comprehensive tests + # this is just to make sure it's being called correctly + + self.assertClose(1.123e-23, 1.123e-23) + self.assertClose(1e40, 1.0000000001e40) + self.assertClose(1.0, 1.000001, rel_tol=1e-4) + self.assertClose(0.0, 0.000001, abs_tol=1e-5) + + def test_assertNotClose(self): + # Note: math.isclose() has more comprehensive tests + # this is just to make sure it's being called correctly + + self.assertNotClose(1, 1e20) + self.assertNotClose(1, 1.00000000001, rel_tol=1e-11) + self.assertNotClose(0.0, 0.000001) + self.assertNotClose(0.0, 0.000001, abs_tol=1e-7) + + # make sure short cut is run if exactly equal + with self.assertRaises(AssertionError): + self.assertNotClose(2.3, 2.3) + def test_assertRaises(self): def _raise(e): raise e