mirror of
https://github.com/python/cpython.git
synced 2025-08-04 17:08:35 +00:00
Issue #19543: Implementation of isclose as per PEP 485
For details, see: PEP 0485 -- A Function for testing approximate equality Functions added: math.isclose() and cmath.isclose(). Original code by Chris Barker. Patch by Tal Einat.
This commit is contained in:
parent
439c5fe3ae
commit
d5519ed7f4
9 changed files with 450 additions and 1 deletions
|
@ -1,5 +1,6 @@
|
|||
from test.support import requires_IEEE_754
|
||||
from test.test_math import parse_testfile, test_file
|
||||
import test.test_math as test_math
|
||||
import unittest
|
||||
import cmath, math
|
||||
from cmath import phase, polar, rect, pi
|
||||
|
@ -529,5 +530,46 @@ class CMathTests(unittest.TestCase):
|
|||
self.assertComplexIdentical(cmath.atanh(z), z)
|
||||
|
||||
|
||||
class IsCloseTests(test_math.IsCloseTests):
|
||||
isclose = cmath.isclose
|
||||
|
||||
def test_reject_complex_tolerances(self):
|
||||
with self.assertRaises(TypeError):
|
||||
self.isclose(1j, 1j, rel_tol=1j)
|
||||
|
||||
with self.assertRaises(TypeError):
|
||||
self.isclose(1j, 1j, abs_tol=1j)
|
||||
|
||||
with self.assertRaises(TypeError):
|
||||
self.isclose(1j, 1j, rel_tol=1j, abs_tol=1j)
|
||||
|
||||
def test_complex_values(self):
|
||||
# test complex values that are close to within 12 decimal places
|
||||
complex_examples = [(1.0+1.0j, 1.000000000001+1.0j),
|
||||
(1.0+1.0j, 1.0+1.000000000001j),
|
||||
(-1.0+1.0j, -1.000000000001+1.0j),
|
||||
(1.0-1.0j, 1.0-0.999999999999j),
|
||||
]
|
||||
|
||||
self.assertAllClose(complex_examples, rel_tol=1e-12)
|
||||
self.assertAllNotClose(complex_examples, rel_tol=1e-13)
|
||||
|
||||
def test_complex_near_zero(self):
|
||||
# test values near zero that are near to within three decimal places
|
||||
near_zero_examples = [(0.001j, 0),
|
||||
(0.001, 0),
|
||||
(0.001+0.001j, 0),
|
||||
(-0.001+0.001j, 0),
|
||||
(0.001-0.001j, 0),
|
||||
(-0.001-0.001j, 0),
|
||||
]
|
||||
|
||||
self.assertAllClose(near_zero_examples, abs_tol=1.5e-03)
|
||||
self.assertAllNotClose(near_zero_examples, abs_tol=0.5e-03)
|
||||
|
||||
self.assertIsClose(0.001-0.001j, 0.001+0.001j, abs_tol=2e-03)
|
||||
self.assertIsNotClose(0.001-0.001j, 0.001+0.001j, abs_tol=1e-03)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
|
|
@ -1166,10 +1166,131 @@ class MathTests(unittest.TestCase):
|
|||
'\n '.join(failures))
|
||||
|
||||
|
||||
class IsCloseTests(unittest.TestCase):
|
||||
isclose = math.isclose # sublcasses should override this
|
||||
|
||||
def assertIsClose(self, a, b, *args, **kwargs):
|
||||
self.assertTrue(self.isclose(a, b, *args, **kwargs),
|
||||
msg="%s and %s should be close!" % (a, b))
|
||||
|
||||
def assertIsNotClose(self, a, b, *args, **kwargs):
|
||||
self.assertFalse(self.isclose(a, b, *args, **kwargs),
|
||||
msg="%s and %s should not be close!" % (a, b))
|
||||
|
||||
def assertAllClose(self, examples, *args, **kwargs):
|
||||
for a, b in examples:
|
||||
self.assertIsClose(a, b, *args, **kwargs)
|
||||
|
||||
def assertAllNotClose(self, examples, *args, **kwargs):
|
||||
for a, b in examples:
|
||||
self.assertIsNotClose(a, b, *args, **kwargs)
|
||||
|
||||
def test_negative_tolerances(self):
|
||||
# ValueError should be raised if either tolerance is less than zero
|
||||
with self.assertRaises(ValueError):
|
||||
self.assertIsClose(1, 1, rel_tol=-1e-100)
|
||||
with self.assertRaises(ValueError):
|
||||
self.assertIsClose(1, 1, rel_tol=1e-100, abs_tol=-1e10)
|
||||
|
||||
def test_identical(self):
|
||||
# identical values must test as close
|
||||
identical_examples = [(2.0, 2.0),
|
||||
(0.1e200, 0.1e200),
|
||||
(1.123e-300, 1.123e-300),
|
||||
(12345, 12345.0),
|
||||
(0.0, -0.0),
|
||||
(345678, 345678)]
|
||||
self.assertAllClose(identical_examples, rel_tol=0.0, abs_tol=0.0)
|
||||
|
||||
def test_eight_decimal_places(self):
|
||||
# examples that are close to 1e-8, but not 1e-9
|
||||
eight_decimal_places_examples = [(1e8, 1e8 + 1),
|
||||
(-1e-8, -1.000000009e-8),
|
||||
(1.12345678, 1.12345679)]
|
||||
self.assertAllClose(eight_decimal_places_examples, rel_tol=1e-8)
|
||||
self.assertAllNotClose(eight_decimal_places_examples, rel_tol=1e-9)
|
||||
|
||||
def test_near_zero(self):
|
||||
# values close to zero
|
||||
near_zero_examples = [(1e-9, 0.0),
|
||||
(-1e-9, 0.0),
|
||||
(-1e-150, 0.0)]
|
||||
# these should not be close to any rel_tol
|
||||
self.assertAllNotClose(near_zero_examples, rel_tol=0.9)
|
||||
# these should be close to abs_tol=1e-8
|
||||
self.assertAllClose(near_zero_examples, abs_tol=1e-8)
|
||||
|
||||
def test_identical_infinite(self):
|
||||
# these are close regardless of tolerance -- i.e. they are equal
|
||||
self.assertIsClose(INF, INF)
|
||||
self.assertIsClose(INF, INF, abs_tol=0.0)
|
||||
self.assertIsClose(NINF, NINF)
|
||||
self.assertIsClose(NINF, NINF, abs_tol=0.0)
|
||||
|
||||
def test_inf_ninf_nan(self):
|
||||
# these should never be close (following IEEE 754 rules for equality)
|
||||
not_close_examples = [(NAN, NAN),
|
||||
(NAN, 1e-100),
|
||||
(1e-100, NAN),
|
||||
(INF, NAN),
|
||||
(NAN, INF),
|
||||
(INF, NINF),
|
||||
(INF, 1.0),
|
||||
(1.0, INF),
|
||||
(INF, 1e308),
|
||||
(1e308, INF)]
|
||||
# use largest reasonable tolerance
|
||||
self.assertAllNotClose(not_close_examples, abs_tol=0.999999999999999)
|
||||
|
||||
def test_zero_tolerance(self):
|
||||
# test with zero tolerance
|
||||
zero_tolerance_close_examples = [(1.0, 1.0),
|
||||
(-3.4, -3.4),
|
||||
(-1e-300, -1e-300)]
|
||||
self.assertAllClose(zero_tolerance_close_examples, rel_tol=0.0)
|
||||
|
||||
zero_tolerance_not_close_examples = [(1.0, 1.000000000000001),
|
||||
(0.99999999999999, 1.0),
|
||||
(1.0e200, .999999999999999e200)]
|
||||
self.assertAllNotClose(zero_tolerance_not_close_examples, rel_tol=0.0)
|
||||
|
||||
def test_assymetry(self):
|
||||
# test the assymetry example from PEP 485
|
||||
self.assertAllClose([(9, 10), (10, 9)], rel_tol=0.1)
|
||||
|
||||
def test_integers(self):
|
||||
# test with integer values
|
||||
integer_examples = [(100000001, 100000000),
|
||||
(123456789, 123456788)]
|
||||
|
||||
self.assertAllClose(integer_examples, rel_tol=1e-8)
|
||||
self.assertAllNotClose(integer_examples, rel_tol=1e-9)
|
||||
|
||||
def test_decimals(self):
|
||||
# test with Decimal values
|
||||
from decimal import Decimal
|
||||
|
||||
decimal_examples = [(Decimal('1.00000001'), Decimal('1.0')),
|
||||
(Decimal('1.00000001e-20'), Decimal('1.0e-20')),
|
||||
(Decimal('1.00000001e-100'), Decimal('1.0e-100'))]
|
||||
self.assertAllClose(decimal_examples, rel_tol=1e-8)
|
||||
self.assertAllNotClose(decimal_examples, rel_tol=1e-9)
|
||||
|
||||
def test_fractions(self):
|
||||
# test with Fraction values
|
||||
from fractions import Fraction
|
||||
|
||||
# could use some more examples here!
|
||||
fraction_examples = [(Fraction(1, 100000000) + 1, Fraction(1))]
|
||||
self.assertAllClose(fraction_examples, rel_tol=1e-8)
|
||||
self.assertAllNotClose(fraction_examples, rel_tol=1e-9)
|
||||
|
||||
|
||||
def test_main():
|
||||
from doctest import DocFileSuite
|
||||
suite = unittest.TestSuite()
|
||||
suite.addTest(unittest.makeSuite(MathTests))
|
||||
suite.addTest(unittest.makeSuite(IsCloseTests))
|
||||
suite.addTest(DocFileSuite("ieee754.txt"))
|
||||
run_unittest(suite)
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue