Skip to content

Commit 971fd98

Browse files
committed
gh-130317: fix PyFloat_Pack2/Unpack2 for NaN's with payload
1 parent 5ec4bf8 commit 971fd98

File tree

3 files changed

+29
-7
lines changed

3 files changed

+29
-7
lines changed

Lib/test/test_struct.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -935,6 +935,14 @@ 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+
if sys.platform != 'win32':
940+
for formatcode, bits in format_bits__nan_list:
941+
nan = struct.unpack('<e', bits)[0]
942+
self.assertEqual(struct.pack('<e', nan), bits)
943+
nan = struct.unpack('>e', bits[::-1])[0]
944+
self.assertEqual(struct.pack('>e', nan), bits[::-1])
945+
938946
# Check that packing produces a bit pattern representing a quiet NaN:
939947
# all exponent bits and the msb of the fraction should all be 1.
940948
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 & 0x1ffULL; /* NaN's payload */
2058+
if (v & 0x8000000000000ULL) { /* 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 ? 0xfff0000000000000ULL : 0x7ff0000000000000ULL;
2407+
2408+
if (f & 0x200) { /* is a quiet NaN? */
2409+
v += 0x8000000000000ULL;
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)