Skip to content

Commit 97e5ec0

Browse files
authored
Merge pull request RustPython#5449 from key262yek/update_test_float_from_CPython_v3.12.7
Update test float from c python v3.12.7
2 parents 3dced01 + 0cf4534 commit 97e5ec0

File tree

2 files changed

+81
-86
lines changed

2 files changed

+81
-86
lines changed

Lib/test/support/testcase.py

+65
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
from math import copysign, isnan
2+
3+
4+
class ExceptionIsLikeMixin:
5+
def assertExceptionIsLike(self, exc, template):
6+
"""
7+
Passes when the provided `exc` matches the structure of `template`.
8+
Individual exceptions don't have to be the same objects or even pass
9+
an equality test: they only need to be the same type and contain equal
10+
`exc_obj.args`.
11+
"""
12+
if exc is None and template is None:
13+
return
14+
15+
if template is None:
16+
self.fail(f"unexpected exception: {exc}")
17+
18+
if exc is None:
19+
self.fail(f"expected an exception like {template!r}, got None")
20+
21+
if not isinstance(exc, ExceptionGroup):
22+
self.assertEqual(exc.__class__, template.__class__)
23+
self.assertEqual(exc.args[0], template.args[0])
24+
else:
25+
self.assertEqual(exc.message, template.message)
26+
self.assertEqual(len(exc.exceptions), len(template.exceptions))
27+
for e, t in zip(exc.exceptions, template.exceptions):
28+
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_float.py

+16-86
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import unittest
99

1010
from test import support
11-
from test.support import import_helper
11+
from test.support.testcase import FloatsAreIdenticalMixin
1212
from test.test_grammar import (VALID_UNDERSCORE_LITERALS,
1313
INVALID_UNDERSCORE_LITERALS)
1414
from math import isinf, isnan, copysign, ldexp
@@ -19,7 +19,6 @@
1919
except ImportError:
2020
_testcapi = None
2121

22-
HAVE_IEEE_754 = float.__getformat__("double").startswith("IEEE")
2322
INF = float("inf")
2423
NAN = float("nan")
2524

@@ -742,8 +741,13 @@ def test_format_testfile(self):
742741

743742
lhs, rhs = map(str.strip, line.split('->'))
744743
fmt, arg = lhs.split()
745-
self.assertEqual(fmt % float(arg), rhs)
746-
self.assertEqual(fmt % -float(arg), '-' + rhs)
744+
f = float(arg)
745+
self.assertEqual(fmt % f, rhs)
746+
self.assertEqual(fmt % -f, '-' + rhs)
747+
if fmt != '%r':
748+
fmt2 = fmt[1:]
749+
self.assertEqual(format(f, fmt2), rhs)
750+
self.assertEqual(format(-f, fmt2), '-' + rhs)
747751

748752
def test_issue5864(self):
749753
self.assertEqual(format(123.456, '.4'), '123.5')
@@ -833,7 +837,7 @@ def test_short_repr(self):
833837
self.assertEqual(repr(float(negs)), str(float(negs)))
834838

835839
@support.requires_IEEE_754
836-
class RoundTestCase(unittest.TestCase):
840+
class RoundTestCase(unittest.TestCase, FloatsAreIdenticalMixin):
837841

838842
def test_inf_nan(self):
839843
self.assertRaises(OverflowError, round, INF)
@@ -863,10 +867,10 @@ def test_large_n(self):
863867

864868
def test_small_n(self):
865869
for n in [-308, -309, -400, 1-2**31, -2**31, -2**31-1, -2**100]:
866-
self.assertEqual(round(123.456, n), 0.0)
867-
self.assertEqual(round(-123.456, n), -0.0)
868-
self.assertEqual(round(1e300, n), 0.0)
869-
self.assertEqual(round(1e-320, n), 0.0)
870+
self.assertFloatsAreIdentical(round(123.456, n), 0.0)
871+
self.assertFloatsAreIdentical(round(-123.456, n), -0.0)
872+
self.assertFloatsAreIdentical(round(1e300, n), 0.0)
873+
self.assertFloatsAreIdentical(round(1e-320, n), 0.0)
870874

871875
def test_overflow(self):
872876
self.assertRaises(OverflowError, round, 1.6e308, -308)
@@ -1053,32 +1057,22 @@ def test_inf_signs(self):
10531057
self.assertEqual(copysign(1.0, float('inf')), 1.0)
10541058
self.assertEqual(copysign(1.0, float('-inf')), -1.0)
10551059

1056-
@unittest.skipUnless(getattr(sys, 'float_repr_style', '') == 'short',
1057-
"applies only when using short float repr style")
10581060
def test_nan_signs(self):
1059-
# When using the dtoa.c code, the sign of float('nan') should
1060-
# be predictable.
1061+
# The sign of float('nan') should be predictable.
10611062
self.assertEqual(copysign(1.0, float('nan')), 1.0)
10621063
self.assertEqual(copysign(1.0, float('-nan')), -1.0)
10631064

10641065

10651066
fromHex = float.fromhex
10661067
toHex = float.hex
1067-
class HexFloatTestCase(unittest.TestCase):
1068+
class HexFloatTestCase(FloatsAreIdenticalMixin, unittest.TestCase):
10681069
MAX = fromHex('0x.fffffffffffff8p+1024') # max normal
10691070
MIN = fromHex('0x1p-1022') # min normal
10701071
TINY = fromHex('0x0.0000000000001p-1022') # min subnormal
10711072
EPS = fromHex('0x0.0000000000001p0') # diff between 1.0 and next float up
10721073

10731074
def identical(self, x, y):
1074-
# check that floats x and y are identical, or that both
1075-
# are NaNs
1076-
if isnan(x) or isnan(y):
1077-
if isnan(x) == isnan(y):
1078-
return
1079-
elif x == y and (x != 0.0 or copysign(1.0, x) == copysign(1.0, y)):
1080-
return
1081-
self.fail('%r not identical to %r' % (x, y))
1075+
self.assertFloatsAreIdentical(x, y)
10821076

10831077
def test_ends(self):
10841078
self.identical(self.MIN, ldexp(1.0, -1022))
@@ -1517,69 +1511,5 @@ def __init__(self, value):
15171511
self.assertEqual(getattr(f, 'foo', 'none'), 'bar')
15181512

15191513

1520-
# Test PyFloat_Pack2(), PyFloat_Pack4() and PyFloat_Pack8()
1521-
# Test PyFloat_Unpack2(), PyFloat_Unpack4() and PyFloat_Unpack8()
1522-
BIG_ENDIAN = 0
1523-
LITTLE_ENDIAN = 1
1524-
EPSILON = {
1525-
2: 2.0 ** -11, # binary16
1526-
4: 2.0 ** -24, # binary32
1527-
8: 2.0 ** -53, # binary64
1528-
}
1529-
1530-
@unittest.skipIf(_testcapi is None, 'needs _testcapi')
1531-
class PackTests(unittest.TestCase):
1532-
def test_pack(self):
1533-
self.assertEqual(_testcapi.float_pack(2, 1.5, BIG_ENDIAN),
1534-
b'>\x00')
1535-
self.assertEqual(_testcapi.float_pack(4, 1.5, BIG_ENDIAN),
1536-
b'?\xc0\x00\x00')
1537-
self.assertEqual(_testcapi.float_pack(8, 1.5, BIG_ENDIAN),
1538-
b'?\xf8\x00\x00\x00\x00\x00\x00')
1539-
self.assertEqual(_testcapi.float_pack(2, 1.5, LITTLE_ENDIAN),
1540-
b'\x00>')
1541-
self.assertEqual(_testcapi.float_pack(4, 1.5, LITTLE_ENDIAN),
1542-
b'\x00\x00\xc0?')
1543-
self.assertEqual(_testcapi.float_pack(8, 1.5, LITTLE_ENDIAN),
1544-
b'\x00\x00\x00\x00\x00\x00\xf8?')
1545-
1546-
def test_unpack(self):
1547-
self.assertEqual(_testcapi.float_unpack(b'>\x00', BIG_ENDIAN),
1548-
1.5)
1549-
self.assertEqual(_testcapi.float_unpack(b'?\xc0\x00\x00', BIG_ENDIAN),
1550-
1.5)
1551-
self.assertEqual(_testcapi.float_unpack(b'?\xf8\x00\x00\x00\x00\x00\x00', BIG_ENDIAN),
1552-
1.5)
1553-
self.assertEqual(_testcapi.float_unpack(b'\x00>', LITTLE_ENDIAN),
1554-
1.5)
1555-
self.assertEqual(_testcapi.float_unpack(b'\x00\x00\xc0?', LITTLE_ENDIAN),
1556-
1.5)
1557-
self.assertEqual(_testcapi.float_unpack(b'\x00\x00\x00\x00\x00\x00\xf8?', LITTLE_ENDIAN),
1558-
1.5)
1559-
1560-
def test_roundtrip(self):
1561-
large = 2.0 ** 100
1562-
values = [1.0, 1.5, large, 1.0/7, math.pi]
1563-
if HAVE_IEEE_754:
1564-
values.extend((INF, NAN))
1565-
for value in values:
1566-
for size in (2, 4, 8,):
1567-
if size == 2 and value == large:
1568-
# too large for 16-bit float
1569-
continue
1570-
rel_tol = EPSILON[size]
1571-
for endian in (BIG_ENDIAN, LITTLE_ENDIAN):
1572-
with self.subTest(value=value, size=size, endian=endian):
1573-
data = _testcapi.float_pack(size, value, endian)
1574-
value2 = _testcapi.float_unpack(data, endian)
1575-
if isnan(value):
1576-
self.assertTrue(isnan(value2), (value, value2))
1577-
elif size < 8:
1578-
self.assertTrue(math.isclose(value2, value, rel_tol=rel_tol),
1579-
(value, value2))
1580-
else:
1581-
self.assertEqual(value2, value)
1582-
1583-
15841514
if __name__ == '__main__':
15851515
unittest.main()

0 commit comments

Comments
 (0)