Skip to content

Commit 5fc8222

Browse files
committed
gh-130317: fix PyFloat_Pack2/Unpack2 for NaN's with payload
1 parent 30e8924 commit 5fc8222

File tree

3 files changed

+28
-7
lines changed

3 files changed

+28
-7
lines changed

Lib/test/test_struct.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -935,6 +935,13 @@ def test_half_float(self):
935935
self.assertTrue(math.isnan(struct.unpack('<e', bits)[0]))
936936
self.assertTrue(math.isnan(struct.unpack('>e', bits[::-1])[0]))
937937

938+
# Check round-trip for NaN's:
939+
for formatcode, bits in format_bits__nan_list:
940+
nan = struct.unpack('<e', bits)[0]
941+
self.assertEqual(struct.pack('<e', nan), bits)
942+
nan = struct.unpack('>e', bits[::-1])[0]
943+
self.assertEqual(struct.pack('>e', nan), bits[::-1])
944+
938945
# Check that packing produces a bit pattern representing a quiet NaN:
939946
# all exponent bits and the msb of the fraction should all be 1.
940947
packed = struct.pack('<e', math.nan)
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Fix :c:func:`PyFloat_Pack2` and :c:func:`PyFloat_Unpack2` for NaN's with
2+
payload. This corrects round-trip for :func:`struct.unpack` and
3+
:func:`struct.pack` in case of the IEEE 754 binary16 "half precision" type.
4+
Patch by Sergey B Kirpichev.

Objects/floatobject.c

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2048,14 +2048,16 @@ PyFloat_Pack2(double x, char *data, int le)
20482048
bits = 0;
20492049
}
20502050
else if (isnan(x)) {
2051-
/* There are 2046 distinct half-precision NaNs (1022 signaling and
2052-
1024 quiet), but there are only two quiet NaNs that don't arise by
2053-
quieting a signaling NaN; we get those by setting the topmost bit
2054-
of the fraction field and clearing all other fraction bits. We
2055-
choose the one with the appropriate sign. */
20562051
sign = (copysign(1.0, x) == -1.0);
20572052
e = 0x1f;
2058-
bits = 512;
2053+
2054+
uint64_t v;
2055+
2056+
memcpy(&v, &x, sizeof(v));
2057+
bits = v & 0x1ff; /* NaN's payload */
2058+
if (v & 0x8000000000000) { /* is a quiet NaN? */
2059+
bits += 0x200;
2060+
}
20592061
}
20602062
else {
20612063
sign = (x < 0.0);
@@ -2401,7 +2403,15 @@ PyFloat_Unpack2(const char *data, int le)
24012403
}
24022404
else {
24032405
/* NaN */
2404-
return sign ? -fabs(Py_NAN) : fabs(Py_NAN);
2406+
uint64_t v = sign ? 0xfff0000000000000 : 0x7ff0000000000000;
2407+
2408+
if (f & 0x200) { /* is a quiet NaN? */
2409+
v += 0x8000000000000;
2410+
f -= 0x200;
2411+
}
2412+
v += f; /* add NaN's payload */
2413+
memcpy(&x, &v, sizeof(v));
2414+
return x;
24052415
}
24062416
}
24072417

0 commit comments

Comments
 (0)