|
12 | 12 |
|
13 | 13 | NULL = None
|
14 | 14 |
|
| 15 | +# For PyFloat_Pack/Unpack* |
| 16 | +BIG_ENDIAN = 0 |
| 17 | +LITTLE_ENDIAN = 1 |
| 18 | +EPSILON = { |
| 19 | + 2: 2.0 ** -11, # binary16 |
| 20 | + 4: 2.0 ** -24, # binary32 |
| 21 | + 8: 2.0 ** -53, # binary64 |
| 22 | +} |
| 23 | + |
| 24 | +HAVE_IEEE_754 = float.__getformat__("double").startswith("IEEE") |
| 25 | +INF = float("inf") |
| 26 | +NAN = float("nan") |
| 27 | + |
15 | 28 |
|
16 | 29 | class CAPIFloatTest(unittest.TestCase):
|
17 | 30 | def test_check(self):
|
@@ -112,6 +125,58 @@ def test_getmin(self):
|
112 | 125 |
|
113 | 126 | self.assertEqual(getmin(), sys.float_info.min)
|
114 | 127 |
|
| 128 | + def test_pack(self): |
| 129 | + # Test PyFloat_Pack2(), PyFloat_Pack4() and PyFloat_Pack8() |
| 130 | + pack = _testcapi.float_pack |
| 131 | + |
| 132 | + self.assertEqual(pack(2, 1.5, BIG_ENDIAN), b'>\x00') |
| 133 | + self.assertEqual(pack(4, 1.5, BIG_ENDIAN), b'?\xc0\x00\x00') |
| 134 | + self.assertEqual(pack(8, 1.5, BIG_ENDIAN), |
| 135 | + b'?\xf8\x00\x00\x00\x00\x00\x00') |
| 136 | + self.assertEqual(pack(2, 1.5, LITTLE_ENDIAN), b'\x00>') |
| 137 | + self.assertEqual(pack(4, 1.5, LITTLE_ENDIAN), b'\x00\x00\xc0?') |
| 138 | + self.assertEqual(pack(8, 1.5, LITTLE_ENDIAN), |
| 139 | + b'\x00\x00\x00\x00\x00\x00\xf8?') |
| 140 | + |
| 141 | + def test_unpack(self): |
| 142 | + # Test PyFloat_Unpack2(), PyFloat_Unpack4() and PyFloat_Unpack8() |
| 143 | + unpack = _testcapi.float_unpack |
| 144 | + |
| 145 | + self.assertEqual(unpack(b'>\x00', BIG_ENDIAN), 1.5) |
| 146 | + self.assertEqual(unpack(b'?\xc0\x00\x00', BIG_ENDIAN), 1.5) |
| 147 | + self.assertEqual(unpack(b'?\xf8\x00\x00\x00\x00\x00\x00', BIG_ENDIAN), |
| 148 | + 1.5) |
| 149 | + self.assertEqual(unpack(b'\x00>', LITTLE_ENDIAN), 1.5) |
| 150 | + self.assertEqual(unpack(b'\x00\x00\xc0?', LITTLE_ENDIAN), 1.5) |
| 151 | + self.assertEqual(unpack(b'\x00\x00\x00\x00\x00\x00\xf8?', LITTLE_ENDIAN), |
| 152 | + 1.5) |
| 153 | + |
| 154 | + def test_pack_unpack_roundtrip(self): |
| 155 | + pack = _testcapi.float_pack |
| 156 | + unpack = _testcapi.float_unpack |
| 157 | + |
| 158 | + large = 2.0 ** 100 |
| 159 | + values = [1.0, 1.5, large, 1.0/7, math.pi] |
| 160 | + if HAVE_IEEE_754: |
| 161 | + values.extend((INF, NAN)) |
| 162 | + for value in values: |
| 163 | + for size in (2, 4, 8,): |
| 164 | + if size == 2 and value == large: |
| 165 | + # too large for 16-bit float |
| 166 | + continue |
| 167 | + rel_tol = EPSILON[size] |
| 168 | + for endian in (BIG_ENDIAN, LITTLE_ENDIAN): |
| 169 | + with self.subTest(value=value, size=size, endian=endian): |
| 170 | + data = pack(size, value, endian) |
| 171 | + value2 = unpack(data, endian) |
| 172 | + if math.isnan(value): |
| 173 | + self.assertTrue(math.isnan(value2), (value, value2)) |
| 174 | + elif size < 8: |
| 175 | + self.assertTrue(math.isclose(value2, value, rel_tol=rel_tol), |
| 176 | + (value, value2)) |
| 177 | + else: |
| 178 | + self.assertEqual(value2, value) |
| 179 | + |
115 | 180 |
|
116 | 181 | if __name__ == "__main__":
|
117 | 182 | unittest.main()
|
0 commit comments