Skip to content

Commit eba13d8

Browse files
committed
gh-145633: Fix struct.pack('f') on s390x
Use PyFloat_Pack4() to raise OverflowError. Add more tests on packing/unpacking floats.
1 parent 4240c7d commit eba13d8

File tree

2 files changed

+30
-11
lines changed

2 files changed

+30
-11
lines changed

Lib/test/test_struct.py

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -365,7 +365,7 @@ def test_p_code(self):
365365
(got,) = struct.unpack(code, got)
366366
self.assertEqual(got, expectedback)
367367

368-
def test_705836(self):
368+
def check_705836(self, format, reverse_format):
369369
# SF bug 705836. "<f" and ">f" had a severe rounding bug, where a carry
370370
# from the low-order discarded bits could propagate into the exponent
371371
# field, causing the result to be wrong by a factor of 2.
@@ -376,27 +376,33 @@ def test_705836(self):
376376
delta /= 2.0
377377
smaller = base - delta
378378
# Packing this rounds away a solid string of trailing 1 bits.
379-
packed = struct.pack("<f", smaller)
380-
unpacked = struct.unpack("<f", packed)[0]
379+
packed = struct.pack(format, smaller)
380+
unpacked = struct.unpack(format, packed)[0]
381381
# This failed at base = 2, 4, and 32, with unpacked = 1, 2, and
382382
# 16, respectively.
383383
self.assertEqual(base, unpacked)
384-
bigpacked = struct.pack(">f", smaller)
384+
385+
bigpacked = struct.pack(reverse_format, smaller)
385386
self.assertEqual(bigpacked, string_reverse(packed))
386-
unpacked = struct.unpack(">f", bigpacked)[0]
387+
unpacked = struct.unpack(reverse_format, bigpacked)[0]
387388
self.assertEqual(base, unpacked)
388389

389390
# Largest finite IEEE single.
390391
big = (1 << 24) - 1
391392
big = math.ldexp(big, 127 - 23)
392-
packed = struct.pack(">f", big)
393-
unpacked = struct.unpack(">f", packed)[0]
393+
packed = struct.pack(format, big)
394+
unpacked = struct.unpack(format, packed)[0]
394395
self.assertEqual(big, unpacked)
395396

396397
# The same, but tack on a 1 bit so it rounds up to infinity.
397398
big = (1 << 25) - 1
398399
big = math.ldexp(big, 127 - 24)
399-
self.assertRaises(OverflowError, struct.pack, ">f", big)
400+
self.assertRaises(OverflowError, struct.pack, format, big)
401+
402+
def test_705836(self):
403+
self.check_705836("<f", ">f")
404+
self.check_705836(">f", "<f")
405+
self.check_705836("f", "<f" if sys.byteorder == "big" else ">f")
400406

401407
def test_1530559(self):
402408
for code, byteorder in iter_integer_formats():
@@ -1201,6 +1207,20 @@ def test_half_float(self):
12011207
for formatcode, bits, f in format_bits_float__doubleRoundingError_list:
12021208
self.assertEqual(bits, struct.pack(formatcode, f))
12031209

1210+
def test_float_round_trip(self):
1211+
for format in ("f", "<f", ">f", "d", "<d", ">d"):
1212+
with self.subTest(format=format):
1213+
f = struct.unpack(format, struct.pack(format, 1.5))[0]
1214+
self.assertEqual(f, 1.5)
1215+
f = struct.unpack(format, struct.pack(format, NAN))[0]
1216+
self.assertTrue(math.isnan(f), f)
1217+
f = struct.unpack(format, struct.pack(format, INF))[0]
1218+
self.assertTrue(math.isinf(f), f)
1219+
self.assertEqual(math.copysign(1.0, f), 1.0)
1220+
f = struct.unpack(format, struct.pack(format, -INF))[0]
1221+
self.assertTrue(math.isinf(f), f)
1222+
self.assertEqual(math.copysign(1.0, f), -1.0)
1223+
12041224

12051225
if __name__ == '__main__':
12061226
unittest.main()

Modules/_struct.c

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -763,14 +763,13 @@ np_halffloat(_structmodulestate *state, char *p, PyObject *v, const formatdef *f
763763
static int
764764
np_float(_structmodulestate *state, char *p, PyObject *v, const formatdef *f)
765765
{
766-
float x = (float)PyFloat_AsDouble(v);
766+
double x = PyFloat_AsDouble(v);
767767
if (x == -1 && PyErr_Occurred()) {
768768
PyErr_SetString(state->StructError,
769769
"required argument is not a float");
770770
return -1;
771771
}
772-
memcpy(p, &x, sizeof x);
773-
return 0;
772+
return PyFloat_Pack4(x, p, PY_LITTLE_ENDIAN);
774773
}
775774

776775
static int

0 commit comments

Comments
 (0)