diff --git a/Lib/test/support/testcase.py b/Lib/test/support/testcase.py index 1e4363b15783eb..fad1e4cb3499c0 100644 --- a/Lib/test/support/testcase.py +++ b/Lib/test/support/testcase.py @@ -1,3 +1,6 @@ +from math import copysign, isnan + + class ExceptionIsLikeMixin: def assertExceptionIsLike(self, exc, template): """ @@ -23,3 +26,40 @@ def assertExceptionIsLike(self, exc, template): self.assertEqual(len(exc.exceptions), len(template.exceptions)) for e, t in zip(exc.exceptions, template.exceptions): self.assertExceptionIsLike(e, t) + + +class FloatsAreIdenticalMixin: + def assertFloatsAreIdentical(self, x, y): + """Fail unless floats x and y are identical, in the sense that: + (1) both x and y are nans, or + (2) both x and y are infinities, with the same sign, or + (3) both x and y are zeros, with the same sign, or + (4) x and y are both finite and nonzero, and x == y + + """ + msg = 'floats {!r} and {!r} are not identical' + + if isnan(x) or isnan(y): + if isnan(x) and isnan(y): + return + elif x == y: + if x != 0.0: + return + # both zero; check that signs match + elif copysign(1.0, x) == copysign(1.0, y): + return + else: + msg += ': zeros have different signs' + self.fail(msg.format(x, y)) + + +class ComplexesAreIdenticalMixin(FloatsAreIdenticalMixin): + def assertComplexesAreIdentical(self, x, y): + """Fail unless complex numbers x and y have equal values and signs. + + In particular, if x and y both have real (or imaginary) part + zero, but the zeros have different signs, this test will fail. + + """ + self.assertFloatsAreIdentical(x.real, y.real) + self.assertFloatsAreIdentical(x.imag, y.imag) diff --git a/Lib/test/test_capi/test_getargs.py b/Lib/test/test_capi/test_getargs.py index 69bd0f4c5f197c..132ba350a92c69 100644 --- a/Lib/test/test_capi/test_getargs.py +++ b/Lib/test/test_capi/test_getargs.py @@ -6,6 +6,7 @@ from test.support import import_helper from test.support import script_helper from test.support import warnings_helper +from test.support.testcase import FloatsAreIdenticalMixin # Skip this test if the _testcapi module isn't available. _testcapi = import_helper.import_module('_testcapi') from _testcapi import getargs_keywords, getargs_keyword_only @@ -436,11 +437,7 @@ def test_K(self): self.assertEqual(VERY_LARGE & ULLONG_MAX, getargs_K(VERY_LARGE)) -class Float_TestCase(unittest.TestCase): - def assertEqualWithSign(self, actual, expected): - self.assertEqual(actual, expected) - self.assertEqual(math.copysign(1, actual), math.copysign(1, expected)) - +class Float_TestCase(unittest.TestCase, FloatsAreIdenticalMixin): def test_f(self): from _testcapi import getargs_f self.assertEqual(getargs_f(4.25), 4.25) @@ -462,10 +459,10 @@ def test_f(self): self.assertEqual(getargs_f(DBL_MAX), INF) self.assertEqual(getargs_f(-DBL_MAX), -INF) if FLT_MIN > DBL_MIN: - self.assertEqualWithSign(getargs_f(DBL_MIN), 0.0) - self.assertEqualWithSign(getargs_f(-DBL_MIN), -0.0) - self.assertEqualWithSign(getargs_f(0.0), 0.0) - self.assertEqualWithSign(getargs_f(-0.0), -0.0) + self.assertFloatsAreIdentical(getargs_f(DBL_MIN), 0.0) + self.assertFloatsAreIdentical(getargs_f(-DBL_MIN), -0.0) + self.assertFloatsAreIdentical(getargs_f(0.0), 0.0) + self.assertFloatsAreIdentical(getargs_f(-0.0), -0.0) r = getargs_f(NAN) self.assertNotEqual(r, r) @@ -494,8 +491,8 @@ def test_d(self): self.assertEqual(getargs_d(x), x) self.assertRaises(OverflowError, getargs_d, 1<