Skip to content

Commit ad0116a

Browse files
authored
Merge pull request RustPython#4823 from Masorubka1/test_float
Update test_float.py from Cpython v3.11.2
2 parents 05487c9 + 77d9c8d commit ad0116a

File tree

1 file changed

+123
-76
lines changed

1 file changed

+123
-76
lines changed

Lib/test/test_float.py

Lines changed: 123 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,21 @@
88
import unittest
99

1010
from test import support
11+
from test.support import import_helper
1112
from test.test_grammar import (VALID_UNDERSCORE_LITERALS,
1213
INVALID_UNDERSCORE_LITERALS)
1314
from math import isinf, isnan, copysign, ldexp
15+
import math
1416

17+
try:
18+
import _testcapi
19+
except ImportError:
20+
_testcapi = None
21+
22+
HAVE_IEEE_754 = float.__getformat__("double").startswith("IEEE")
1523
INF = float("inf")
1624
NAN = float("nan")
1725

18-
have_getformat = hasattr(float, "__getformat__")
19-
requires_getformat = unittest.skipUnless(have_getformat,
20-
"requires __getformat__")
21-
requires_setformat = unittest.skipUnless(hasattr(float, "__setformat__"),
22-
"requires __setformat__")
2326

2427
#locate file with float format test values
2528
test_dir = os.path.dirname(__file__) or os.curdir
@@ -139,6 +142,10 @@ def check(s):
139142
check('123\xbd')
140143
check(' 123 456 ')
141144
check(b' 123 456 ')
145+
# all whitespace (cf. https://github.com/python/cpython/issues/95605)
146+
check('')
147+
check(' ')
148+
check('\t \n')
142149

143150
# non-ascii digits (error came from non-digit '!')
144151
check('\u0663\u0661\u0664!')
@@ -249,6 +256,35 @@ def test_keyword_args(self):
249256
with self.assertRaisesRegex(TypeError, 'keyword argument'):
250257
float(x='3.14')
251258

259+
# TODO: RUSTPYTHON
260+
@unittest.expectedFailure
261+
def test_keywords_in_subclass(self):
262+
class subclass(float):
263+
pass
264+
u = subclass(2.5)
265+
self.assertIs(type(u), subclass)
266+
self.assertEqual(float(u), 2.5)
267+
with self.assertRaises(TypeError):
268+
subclass(x=0)
269+
270+
class subclass_with_init(float):
271+
def __init__(self, arg, newarg=None):
272+
self.newarg = newarg
273+
u = subclass_with_init(2.5, newarg=3)
274+
self.assertIs(type(u), subclass_with_init)
275+
self.assertEqual(float(u), 2.5)
276+
self.assertEqual(u.newarg, 3)
277+
278+
class subclass_with_new(float):
279+
def __new__(cls, arg, newarg=None):
280+
self = super().__new__(cls, arg)
281+
self.newarg = newarg
282+
return self
283+
u = subclass_with_new(2.5, newarg=3)
284+
self.assertIs(type(u), subclass_with_new)
285+
self.assertEqual(float(u), 2.5)
286+
self.assertEqual(u.newarg, 3)
287+
252288
def test_is_integer(self):
253289
self.assertFalse((1.1).is_integer())
254290
self.assertTrue((1.).is_integer())
@@ -590,17 +626,8 @@ class F(float, H):
590626
self.assertEqual(hash(value), object.__hash__(value))
591627

592628

593-
@requires_setformat
629+
@unittest.skipUnless(hasattr(float, "__getformat__"), "requires __getformat__")
594630
class FormatFunctionsTestCase(unittest.TestCase):
595-
596-
def setUp(self):
597-
self.save_formats = {'double':float.__getformat__('double'),
598-
'float':float.__getformat__('float')}
599-
600-
def tearDown(self):
601-
float.__setformat__('double', self.save_formats['double'])
602-
float.__setformat__('float', self.save_formats['float'])
603-
604631
def test_getformat(self):
605632
self.assertIn(float.__getformat__('double'),
606633
['unknown', 'IEEE, big-endian', 'IEEE, little-endian'])
@@ -609,24 +636,6 @@ def test_getformat(self):
609636
self.assertRaises(ValueError, float.__getformat__, 'chicken')
610637
self.assertRaises(TypeError, float.__getformat__, 1)
611638

612-
def test_setformat(self):
613-
for t in 'double', 'float':
614-
float.__setformat__(t, 'unknown')
615-
if self.save_formats[t] == 'IEEE, big-endian':
616-
self.assertRaises(ValueError, float.__setformat__,
617-
t, 'IEEE, little-endian')
618-
elif self.save_formats[t] == 'IEEE, little-endian':
619-
self.assertRaises(ValueError, float.__setformat__,
620-
t, 'IEEE, big-endian')
621-
else:
622-
self.assertRaises(ValueError, float.__setformat__,
623-
t, 'IEEE, big-endian')
624-
self.assertRaises(ValueError, float.__setformat__,
625-
t, 'IEEE, little-endian')
626-
self.assertRaises(ValueError, float.__setformat__,
627-
t, 'chicken')
628-
self.assertRaises(ValueError, float.__setformat__,
629-
'chicken', 'unknown')
630639

631640
BE_DOUBLE_INF = b'\x7f\xf0\x00\x00\x00\x00\x00\x00'
632641
LE_DOUBLE_INF = bytes(reversed(BE_DOUBLE_INF))
@@ -638,36 +647,6 @@ def test_setformat(self):
638647
BE_FLOAT_NAN = b'\x7f\xc0\x00\x00'
639648
LE_FLOAT_NAN = bytes(reversed(BE_FLOAT_NAN))
640649

641-
# on non-IEEE platforms, attempting to unpack a bit pattern
642-
# representing an infinity or a NaN should raise an exception.
643-
644-
@requires_setformat
645-
class UnknownFormatTestCase(unittest.TestCase):
646-
def setUp(self):
647-
self.save_formats = {'double':float.__getformat__('double'),
648-
'float':float.__getformat__('float')}
649-
float.__setformat__('double', 'unknown')
650-
float.__setformat__('float', 'unknown')
651-
652-
def tearDown(self):
653-
float.__setformat__('double', self.save_formats['double'])
654-
float.__setformat__('float', self.save_formats['float'])
655-
656-
def test_double_specials_dont_unpack(self):
657-
for fmt, data in [('>d', BE_DOUBLE_INF),
658-
('>d', BE_DOUBLE_NAN),
659-
('<d', LE_DOUBLE_INF),
660-
('<d', LE_DOUBLE_NAN)]:
661-
self.assertRaises(ValueError, struct.unpack, fmt, data)
662-
663-
def test_float_specials_dont_unpack(self):
664-
for fmt, data in [('>f', BE_FLOAT_INF),
665-
('>f', BE_FLOAT_NAN),
666-
('<f', LE_FLOAT_INF),
667-
('<f', LE_FLOAT_NAN)]:
668-
self.assertRaises(ValueError, struct.unpack, fmt, data)
669-
670-
671650
# on an IEEE platform, all we guarantee is that bit patterns
672651
# representing infinities or NaNs do not raise an exception; all else
673652
# is accident (today).
@@ -694,8 +673,9 @@ def test_float_specials_do_unpack(self):
694673
# TODO: RUSTPYTHON
695674
@unittest.expectedFailure
696675
@support.requires_IEEE_754
676+
@unittest.skipIf(_testcapi is None, 'needs _testcapi')
697677
def test_serialized_float_rounding(self):
698-
from _testcapi import FLT_MAX
678+
FLT_MAX = _testcapi.FLT_MAX
699679
self.assertEqual(struct.pack("<f", 3.40282356e38), struct.pack("<f", FLT_MAX))
700680
self.assertEqual(struct.pack("<f", -3.40282356e38), struct.pack("<f", -FLT_MAX))
701681

@@ -735,18 +715,16 @@ def test_format(self):
735715
# conversion to string should fail
736716
self.assertRaises(ValueError, format, 3.0, "s")
737717

738-
# other format specifiers shouldn't work on floats,
739-
# in particular int specifiers
740-
for format_spec in ([chr(x) for x in range(ord('a'), ord('z')+1)] +
741-
[chr(x) for x in range(ord('A'), ord('Z')+1)]):
742-
if not format_spec in 'eEfFgGn%':
743-
self.assertRaises(ValueError, format, 0.0, format_spec)
744-
self.assertRaises(ValueError, format, 1.0, format_spec)
745-
self.assertRaises(ValueError, format, -1.0, format_spec)
746-
self.assertRaises(ValueError, format, 1e100, format_spec)
747-
self.assertRaises(ValueError, format, -1e100, format_spec)
748-
self.assertRaises(ValueError, format, 1e-100, format_spec)
749-
self.assertRaises(ValueError, format, -1e-100, format_spec)
718+
# confirm format options expected to fail on floats, such as integer
719+
# presentation types
720+
for format_spec in 'sbcdoxX':
721+
self.assertRaises(ValueError, format, 0.0, format_spec)
722+
self.assertRaises(ValueError, format, 1.0, format_spec)
723+
self.assertRaises(ValueError, format, -1.0, format_spec)
724+
self.assertRaises(ValueError, format, 1e100, format_spec)
725+
self.assertRaises(ValueError, format, -1e100, format_spec)
726+
self.assertRaises(ValueError, format, 1e-100, format_spec)
727+
self.assertRaises(ValueError, format, -1e-100, format_spec)
750728

751729
# issue 3382
752730
self.assertEqual(format(NAN, 'f'), 'nan')
@@ -868,6 +846,11 @@ def test_inf_nan(self):
868846
self.assertRaises(TypeError, round, NAN, "ceci n'est pas un integer")
869847
self.assertRaises(TypeError, round, -0.0, 1j)
870848

849+
def test_inf_nan_ndigits(self):
850+
self.assertEqual(round(INF, 0), INF)
851+
self.assertEqual(round(-INF, 0), -INF)
852+
self.assertTrue(math.isnan(round(NAN, 0)))
853+
871854
def test_large_n(self):
872855
for n in [324, 325, 400, 2**31-1, 2**31, 2**32, 2**100]:
873856
self.assertEqual(round(123.456, n), 123.456)
@@ -1538,5 +1521,69 @@ def __init__(self, value):
15381521
self.assertEqual(getattr(f, 'foo', 'none'), 'bar')
15391522

15401523

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

0 commit comments

Comments
 (0)