diff --git a/Lib/test/test_capi/test_float.py b/Lib/test/test_capi/test_float.py index 95635e822a1245..cb94d562645916 100644 --- a/Lib/test/test_capi/test_float.py +++ b/Lib/test/test_capi/test_float.py @@ -12,6 +12,19 @@ NULL = None +# For PyFloat_Pack/Unpack* +BIG_ENDIAN = 0 +LITTLE_ENDIAN = 1 +EPSILON = { + 2: 2.0 ** -11, # binary16 + 4: 2.0 ** -24, # binary32 + 8: 2.0 ** -53, # binary64 +} + +HAVE_IEEE_754 = float.__getformat__("double").startswith("IEEE") +INF = float("inf") +NAN = float("nan") + class CAPIFloatTest(unittest.TestCase): def test_check(self): @@ -112,6 +125,58 @@ def test_getmin(self): self.assertEqual(getmin(), sys.float_info.min) + def test_pack(self): + # Test PyFloat_Pack2(), PyFloat_Pack4() and PyFloat_Pack8() + pack = _testcapi.float_pack + + self.assertEqual(pack(2, 1.5, BIG_ENDIAN), b'>\x00') + self.assertEqual(pack(4, 1.5, BIG_ENDIAN), b'?\xc0\x00\x00') + self.assertEqual(pack(8, 1.5, BIG_ENDIAN), + b'?\xf8\x00\x00\x00\x00\x00\x00') + self.assertEqual(pack(2, 1.5, LITTLE_ENDIAN), b'\x00>') + self.assertEqual(pack(4, 1.5, LITTLE_ENDIAN), b'\x00\x00\xc0?') + self.assertEqual(pack(8, 1.5, LITTLE_ENDIAN), + b'\x00\x00\x00\x00\x00\x00\xf8?') + + def test_unpack(self): + # Test PyFloat_Unpack2(), PyFloat_Unpack4() and PyFloat_Unpack8() + unpack = _testcapi.float_unpack + + self.assertEqual(unpack(b'>\x00', BIG_ENDIAN), 1.5) + self.assertEqual(unpack(b'?\xc0\x00\x00', BIG_ENDIAN), 1.5) + self.assertEqual(unpack(b'?\xf8\x00\x00\x00\x00\x00\x00', BIG_ENDIAN), + 1.5) + self.assertEqual(unpack(b'\x00>', LITTLE_ENDIAN), 1.5) + self.assertEqual(unpack(b'\x00\x00\xc0?', LITTLE_ENDIAN), 1.5) + self.assertEqual(unpack(b'\x00\x00\x00\x00\x00\x00\xf8?', LITTLE_ENDIAN), + 1.5) + + def test_pack_unpack_roundtrip(self): + pack = _testcapi.float_pack + unpack = _testcapi.float_unpack + + large = 2.0 ** 100 + values = [1.0, 1.5, large, 1.0/7, math.pi] + if HAVE_IEEE_754: + values.extend((INF, NAN)) + for value in values: + for size in (2, 4, 8,): + if size == 2 and value == large: + # too large for 16-bit float + continue + rel_tol = EPSILON[size] + for endian in (BIG_ENDIAN, LITTLE_ENDIAN): + with self.subTest(value=value, size=size, endian=endian): + data = pack(size, value, endian) + value2 = unpack(data, endian) + if math.isnan(value): + self.assertTrue(math.isnan(value2), (value, value2)) + elif size < 8: + self.assertTrue(math.isclose(value2, value, rel_tol=rel_tol), + (value, value2)) + else: + self.assertEqual(value2, value) + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_float.py b/Lib/test/test_float.py index b6daae7e9280ff..5bd640617d6874 100644 --- a/Lib/test/test_float.py +++ b/Lib/test/test_float.py @@ -18,7 +18,6 @@ except ImportError: _testcapi = None -HAVE_IEEE_754 = float.__getformat__("double").startswith("IEEE") INF = float("inf") NAN = float("nan") @@ -1505,69 +1504,5 @@ def __init__(self, value): self.assertEqual(getattr(f, 'foo', 'none'), 'bar') -# Test PyFloat_Pack2(), PyFloat_Pack4() and PyFloat_Pack8() -# Test PyFloat_Unpack2(), PyFloat_Unpack4() and PyFloat_Unpack8() -BIG_ENDIAN = 0 -LITTLE_ENDIAN = 1 -EPSILON = { - 2: 2.0 ** -11, # binary16 - 4: 2.0 ** -24, # binary32 - 8: 2.0 ** -53, # binary64 -} - -@unittest.skipIf(_testcapi is None, 'needs _testcapi') -class PackTests(unittest.TestCase): - def test_pack(self): - self.assertEqual(_testcapi.float_pack(2, 1.5, BIG_ENDIAN), - b'>\x00') - self.assertEqual(_testcapi.float_pack(4, 1.5, BIG_ENDIAN), - b'?\xc0\x00\x00') - self.assertEqual(_testcapi.float_pack(8, 1.5, BIG_ENDIAN), - b'?\xf8\x00\x00\x00\x00\x00\x00') - self.assertEqual(_testcapi.float_pack(2, 1.5, LITTLE_ENDIAN), - b'\x00>') - self.assertEqual(_testcapi.float_pack(4, 1.5, LITTLE_ENDIAN), - b'\x00\x00\xc0?') - self.assertEqual(_testcapi.float_pack(8, 1.5, LITTLE_ENDIAN), - b'\x00\x00\x00\x00\x00\x00\xf8?') - - def test_unpack(self): - self.assertEqual(_testcapi.float_unpack(b'>\x00', BIG_ENDIAN), - 1.5) - self.assertEqual(_testcapi.float_unpack(b'?\xc0\x00\x00', BIG_ENDIAN), - 1.5) - self.assertEqual(_testcapi.float_unpack(b'?\xf8\x00\x00\x00\x00\x00\x00', BIG_ENDIAN), - 1.5) - self.assertEqual(_testcapi.float_unpack(b'\x00>', LITTLE_ENDIAN), - 1.5) - self.assertEqual(_testcapi.float_unpack(b'\x00\x00\xc0?', LITTLE_ENDIAN), - 1.5) - self.assertEqual(_testcapi.float_unpack(b'\x00\x00\x00\x00\x00\x00\xf8?', LITTLE_ENDIAN), - 1.5) - - def test_roundtrip(self): - large = 2.0 ** 100 - values = [1.0, 1.5, large, 1.0/7, math.pi] - if HAVE_IEEE_754: - values.extend((INF, NAN)) - for value in values: - for size in (2, 4, 8,): - if size == 2 and value == large: - # too large for 16-bit float - continue - rel_tol = EPSILON[size] - for endian in (BIG_ENDIAN, LITTLE_ENDIAN): - with self.subTest(value=value, size=size, endian=endian): - data = _testcapi.float_pack(size, value, endian) - value2 = _testcapi.float_unpack(data, endian) - if isnan(value): - self.assertTrue(isnan(value2), (value, value2)) - elif size < 8: - self.assertTrue(math.isclose(value2, value, rel_tol=rel_tol), - (value, value2)) - else: - self.assertEqual(value2, value) - - if __name__ == '__main__': unittest.main()