Skip to content

Commit d10eef8

Browse files
committed
pythongh-121039: add Floats/ComplexesAreIdenticalMixin to test.support.testcase
1 parent fd0f814 commit d10eef8

File tree

4 files changed

+51
-72
lines changed

4 files changed

+51
-72
lines changed

Lib/test/support/testcase.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
from math import copysign, isnan
2+
3+
14
class ExceptionIsLikeMixin:
25
def assertExceptionIsLike(self, exc, template):
36
"""
@@ -23,3 +26,40 @@ def assertExceptionIsLike(self, exc, template):
2326
self.assertEqual(len(exc.exceptions), len(template.exceptions))
2427
for e, t in zip(exc.exceptions, template.exceptions):
2528
self.assertExceptionIsLike(e, t)
29+
30+
31+
class FloatsAreIdenticalMixin:
32+
def assertFloatsAreIdentical(self, x, y):
33+
"""Fail unless floats x and y are identical, in the sense that:
34+
(1) both x and y are nans, or
35+
(2) both x and y are infinities, with the same sign, or
36+
(3) both x and y are zeros, with the same sign, or
37+
(4) x and y are both finite and nonzero, and x == y
38+
39+
"""
40+
msg = 'floats {!r} and {!r} are not identical'
41+
42+
if isnan(x) or isnan(y):
43+
if isnan(x) and isnan(y):
44+
return
45+
elif x == y:
46+
if x != 0.0:
47+
return
48+
# both zero; check that signs match
49+
elif copysign(1.0, x) == copysign(1.0, y):
50+
return
51+
else:
52+
msg += ': zeros have different signs'
53+
self.fail(msg.format(x, y))
54+
55+
56+
class ComplexesAreIdenticalMixin(FloatsAreIdenticalMixin):
57+
def assertComplexesAreIdentical(self, x, y):
58+
"""Fail unless complex numbers x and y have equal values and signs.
59+
60+
In particular, if x and y both have real (or imaginary) part
61+
zero, but the zeros have different signs, this test will fail.
62+
63+
"""
64+
self.assertFloatsAreIdentical(x.real, y.real)
65+
self.assertFloatsAreIdentical(x.imag, y.imag)

Lib/test/test_cmath.py

Lines changed: 5 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from test.support import requires_IEEE_754, cpython_only, import_helper
2+
from test.support.testcase import ComplexesAreIdenticalMixin
23
from test.test_math import parse_testfile, test_file
34
import test.test_math as test_math
45
import unittest
@@ -49,7 +50,7 @@
4950
(INF, NAN)
5051
]]
5152

52-
class CMathTests(unittest.TestCase):
53+
class CMathTests(ComplexesAreIdenticalMixin, unittest.TestCase):
5354
# list of all functions in cmath
5455
test_functions = [getattr(cmath, fname) for fname in [
5556
'acos', 'acosh', 'asin', 'asinh', 'atan', 'atanh',
@@ -65,39 +66,6 @@ def setUp(self):
6566
def tearDown(self):
6667
self.test_values.close()
6768

68-
def assertFloatIdentical(self, x, y):
69-
"""Fail unless floats x and y are identical, in the sense that:
70-
(1) both x and y are nans, or
71-
(2) both x and y are infinities, with the same sign, or
72-
(3) both x and y are zeros, with the same sign, or
73-
(4) x and y are both finite and nonzero, and x == y
74-
75-
"""
76-
msg = 'floats {!r} and {!r} are not identical'
77-
78-
if math.isnan(x) or math.isnan(y):
79-
if math.isnan(x) and math.isnan(y):
80-
return
81-
elif x == y:
82-
if x != 0.0:
83-
return
84-
# both zero; check that signs match
85-
elif math.copysign(1.0, x) == math.copysign(1.0, y):
86-
return
87-
else:
88-
msg += ': zeros have different signs'
89-
self.fail(msg.format(x, y))
90-
91-
def assertComplexIdentical(self, x, y):
92-
"""Fail unless complex numbers x and y have equal values and signs.
93-
94-
In particular, if x and y both have real (or imaginary) part
95-
zero, but the zeros have different signs, this test will fail.
96-
97-
"""
98-
self.assertFloatIdentical(x.real, y.real)
99-
self.assertFloatIdentical(x.imag, y.imag)
100-
10169
def rAssertAlmostEqual(self, a, b, rel_err = 2e-15, abs_err = 5e-323,
10270
msg=None):
10371
"""Fail if the two floating-point numbers are not almost equal.
@@ -555,7 +523,7 @@ def test_isinf(self):
555523
@requires_IEEE_754
556524
def testTanhSign(self):
557525
for z in complex_zeros:
558-
self.assertComplexIdentical(cmath.tanh(z), z)
526+
self.assertComplexesAreIdentical(cmath.tanh(z), z)
559527

560528
# The algorithm used for atan and atanh makes use of the system
561529
# log1p function; If that system function doesn't respect the sign
@@ -564,12 +532,12 @@ def testTanhSign(self):
564532
@requires_IEEE_754
565533
def testAtanSign(self):
566534
for z in complex_zeros:
567-
self.assertComplexIdentical(cmath.atan(z), z)
535+
self.assertComplexesAreIdentical(cmath.atan(z), z)
568536

569537
@requires_IEEE_754
570538
def testAtanhSign(self):
571539
for z in complex_zeros:
572-
self.assertComplexIdentical(cmath.atanh(z), z)
540+
self.assertComplexesAreIdentical(cmath.atanh(z), z)
573541

574542

575543
class IsCloseTests(test_math.IsCloseTests):

Lib/test/test_complex.py

Lines changed: 3 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import unittest
22
import sys
33
from test import support
4+
from test.support.testcase import ComplexesAreIdenticalMixin
45
from test.test_grammar import (VALID_UNDERSCORE_LITERALS,
56
INVALID_UNDERSCORE_LITERALS)
67

@@ -42,7 +43,7 @@ def __init__(self, value):
4243
def __complex__(self):
4344
return self.value
4445

45-
class ComplexTest(unittest.TestCase):
46+
class ComplexTest(ComplexesAreIdenticalMixin, unittest.TestCase):
4647

4748
def assertAlmostEqual(self, a, b):
4849
if isinstance(a, complex):
@@ -71,29 +72,6 @@ def assertCloseAbs(self, x, y, eps=1e-9):
7172
# check that relative difference < eps
7273
self.assertTrue(abs((x-y)/y) < eps)
7374

74-
def assertFloatsAreIdentical(self, x, y):
75-
"""assert that floats x and y are identical, in the sense that:
76-
(1) both x and y are nans, or
77-
(2) both x and y are infinities, with the same sign, or
78-
(3) both x and y are zeros, with the same sign, or
79-
(4) x and y are both finite and nonzero, and x == y
80-
81-
"""
82-
msg = 'floats {!r} and {!r} are not identical'
83-
84-
if isnan(x) or isnan(y):
85-
if isnan(x) and isnan(y):
86-
return
87-
elif x == y:
88-
if x != 0.0:
89-
return
90-
# both zero; check that signs match
91-
elif copysign(1.0, x) == copysign(1.0, y):
92-
return
93-
else:
94-
msg += ': zeros have different signs'
95-
self.fail(msg.format(x, y))
96-
9775
def assertClose(self, x, y, eps=1e-9):
9876
"""Return true iff complexes x and y "are close"."""
9977
self.assertCloseAbs(x.real, y.real, eps)
@@ -759,8 +737,7 @@ def test_repr_roundtrip(self):
759737
for y in vals:
760738
z = complex(x, y)
761739
roundtrip = complex(repr(z))
762-
self.assertFloatsAreIdentical(z.real, roundtrip.real)
763-
self.assertFloatsAreIdentical(z.imag, roundtrip.imag)
740+
self.assertComplexesAreIdentical(z, roundtrip)
764741

765742
# if we predefine some constants, then eval(repr(z)) should
766743
# also work, except that it might change the sign of zeros

Lib/test/test_float.py

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import unittest
99

1010
from test import support
11+
from test.support.testcase import FloatsAreIdenticalMixin
1112
from test.test_grammar import (VALID_UNDERSCORE_LITERALS,
1213
INVALID_UNDERSCORE_LITERALS)
1314
from math import isinf, isnan, copysign, ldexp
@@ -1060,21 +1061,14 @@ def test_nan_signs(self):
10601061

10611062
fromHex = float.fromhex
10621063
toHex = float.hex
1063-
class HexFloatTestCase(unittest.TestCase):
1064+
class HexFloatTestCase(FloatsAreIdenticalMixin, unittest.TestCase):
10641065
MAX = fromHex('0x.fffffffffffff8p+1024') # max normal
10651066
MIN = fromHex('0x1p-1022') # min normal
10661067
TINY = fromHex('0x0.0000000000001p-1022') # min subnormal
10671068
EPS = fromHex('0x0.0000000000001p0') # diff between 1.0 and next float up
10681069

10691070
def identical(self, x, y):
1070-
# check that floats x and y are identical, or that both
1071-
# are NaNs
1072-
if isnan(x) or isnan(y):
1073-
if isnan(x) == isnan(y):
1074-
return
1075-
elif x == y and (x != 0.0 or copysign(1.0, x) == copysign(1.0, y)):
1076-
return
1077-
self.fail('%r not identical to %r' % (x, y))
1071+
self.assertFloatsAreIdentical(x, y)
10781072

10791073
def test_ends(self):
10801074
self.identical(self.MIN, ldexp(1.0, -1022))

0 commit comments

Comments
 (0)