Skip to content

Commit f513b14

Browse files
committed
more tests in test_capi, fix also Pack/Unpack4
1 parent b05720a commit f513b14

File tree

2 files changed

+52
-0
lines changed

2 files changed

+52
-0
lines changed

Lib/test/test_capi/test_float.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import math
2+
import random
23
import sys
34
import unittest
45
import warnings
@@ -178,6 +179,33 @@ def test_pack_unpack_roundtrip(self):
178179
else:
179180
self.assertEqual(value2, value)
180181

182+
@unittest.skipUnless(HAVE_IEEE_754, "requires IEEE 754")
183+
def test_pack_unpack_roundtrip_nans(self):
184+
pack = _testcapi.float_pack
185+
unpack = _testcapi.float_unpack
186+
187+
for _ in range(100):
188+
for size in (2, 4, 8):
189+
sign = random.randint(0, 1)
190+
quiet = random.randint(0, 1)
191+
if size == 8:
192+
payload = random.randint(0 if quiet else 1, 1<<50)
193+
i = (sign<<63) + (0x7ff<<52) + (quiet<<51) + payload
194+
elif size == 4:
195+
payload = random.randint(0 if quiet else 1, 1<<21)
196+
i = (sign<<31) + (0xff<<23) + (quiet<<22) + payload
197+
elif size == 2:
198+
payload = random.randint(0 if quiet else 1, 1<<8)
199+
i = (sign<<15) + (0x1f<<10) + (quiet<<9) + payload
200+
data = bytes.fromhex(f'{i:x}')
201+
for endian in (BIG_ENDIAN, LITTLE_ENDIAN):
202+
with self.subTest(data=data, size=size, endian=endian):
203+
data1 = data if endian == BIG_ENDIAN else data[::-1]
204+
value = unpack(data1, endian)
205+
data2 = pack(size, value, endian)
206+
self.assertTrue(math.isnan(value))
207+
self.assertEqual(data1, data2)
208+
181209

182210
if __name__ == "__main__":
183211
unittest.main()

Objects/floatobject.c

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2221,6 +2221,17 @@ PyFloat_Pack4(double x, char *data, int le)
22212221
if (isinf(y) && !isinf(x))
22222222
goto Overflow;
22232223

2224+
if (isnan(x)) {
2225+
uint64_t v;
2226+
2227+
memcpy(&v, &x, 8);
2228+
if ((v & (1ULL<<51)) == 0) {
2229+
uint32_t *py = (uint32_t *)&y;
2230+
2231+
*py -= (1<<22);
2232+
}
2233+
}
2234+
22242235
unsigned char s[sizeof(float)];
22252236
memcpy(s, &y, sizeof(float));
22262237

@@ -2507,6 +2518,19 @@ PyFloat_Unpack4(const char *data, int le)
25072518
memcpy(&x, p, 4);
25082519
}
25092520

2521+
if (isnan(x)) {
2522+
uint32_t v;
2523+
2524+
memcpy(&v, &x, 4);
2525+
if ((v & (1<<22)) == 0) {
2526+
double y = x;
2527+
uint64_t *py = (uint64_t *)&y;
2528+
2529+
*py -= (1ULL<<51);
2530+
return y;
2531+
}
2532+
}
2533+
25102534
return x;
25112535
}
25122536
}

0 commit comments

Comments
 (0)