]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.13] gh-145633: Fix struct.pack('f') on s390x (#146422) (#146461)
authorVictor Stinner <vstinner@python.org>
Thu, 26 Mar 2026 11:45:10 +0000 (12:45 +0100)
committerGitHub <noreply@github.com>
Thu, 26 Mar 2026 11:45:10 +0000 (11:45 +0000)
gh-145633: Fix struct.pack('f') on s390x (#146422)

Use PyFloat_Pack4() to raise OverflowError.
Add more tests on packing/unpacking floats.

(cherry picked from commit 8de70b31c59b1d572d95f8bb471a09cfe4cd2b13)

Co-authored-by: Sergey B Kirpichev <skirpichev@gmail.com>
Lib/test/test_struct.py
Misc/NEWS.d/next/Library/2026-03-26-11-04-42.gh-issue-145633.RWjlaX.rst [new file with mode: 0644]
Modules/_struct.c

index 0aa71d20db89e46acfb66aedbe558ddf693e93ec..460e9e94aacf81bbd6a6ac2fe452b3dd6ea95c66 100644 (file)
@@ -392,6 +392,21 @@ class StructTest(unittest.TestCase):
         big = (1 << 25) - 1
         big = math.ldexp(big, 127 - 24)
         self.assertRaises(OverflowError, struct.pack, ">f", big)
+        self.assertRaises(OverflowError, struct.pack, "<f", big)
+        # same for native format, see gh-145633
+        self.assertRaises(OverflowError, struct.pack, "f", big)
+
+        # And for half-floats
+        big = (1 << 11) - 1
+        big = math.ldexp(big, 15 - 10)
+        packed = struct.pack(">e", big)
+        unpacked = struct.unpack(">e", packed)[0]
+        self.assertEqual(big, unpacked)
+        big = (1 << 12) - 1
+        big = math.ldexp(big, 15 - 11)
+        self.assertRaises(OverflowError, struct.pack, ">e", big)
+        self.assertRaises(OverflowError, struct.pack, "<e", big)
+        self.assertRaises(OverflowError, struct.pack, "e", big)
 
     def test_1530559(self):
         for code, byteorder in iter_integer_formats():
@@ -832,6 +847,27 @@ class StructTest(unittest.TestCase):
         self.assertRaises(RuntimeError, repr, S)
         self.assertEqual(S.size, -1)
 
+    def test_float_round_trip(self):
+        INF = float('inf')
+        NAN = float('nan')
+
+        for format in (
+            "f", "<f", ">f",
+            "d", "<d", ">d",
+            "e", "<e", ">e",
+        ):
+            with self.subTest(format=format):
+                f = struct.unpack(format, struct.pack(format, 1.5))[0]
+                self.assertEqual(f, 1.5)
+                f = struct.unpack(format, struct.pack(format, NAN))[0]
+                self.assertTrue(math.isnan(f), f)
+                f = struct.unpack(format, struct.pack(format, INF))[0]
+                self.assertTrue(math.isinf(f), f)
+                self.assertEqual(math.copysign(1.0, f), 1.0)
+                f = struct.unpack(format, struct.pack(format, -INF))[0]
+                self.assertTrue(math.isinf(f), f)
+                self.assertEqual(math.copysign(1.0, f), -1.0)
+
 
 class UnpackIteratorTest(unittest.TestCase):
     """
diff --git a/Misc/NEWS.d/next/Library/2026-03-26-11-04-42.gh-issue-145633.RWjlaX.rst b/Misc/NEWS.d/next/Library/2026-03-26-11-04-42.gh-issue-145633.RWjlaX.rst
new file mode 100644 (file)
index 0000000..00507fe
--- /dev/null
@@ -0,0 +1,2 @@
+Fix ``struct.pack('f', float)``: use :c:func:`PyFloat_Pack4` to raise
+:exc:`OverflowError`. Patch by Sergey B Kirpichev and Victor Stinner.
index 4c84950af344b8e1151b3c58f9f062bce148f2f4..f5e1c62bb240e57ad4aa27f257ab6a18139f9400 100644 (file)
@@ -768,14 +768,13 @@ np_halffloat(_structmodulestate *state, char *p, PyObject *v, const formatdef *f
 static int
 np_float(_structmodulestate *state, char *p, PyObject *v, const formatdef *f)
 {
-    float x = (float)PyFloat_AsDouble(v);
+    double x = PyFloat_AsDouble(v);
     if (x == -1 && PyErr_Occurred()) {
         PyErr_SetString(state->StructError,
                         "required argument is not a float");
         return -1;
     }
-    memcpy(p, (char *)&x, sizeof x);
-    return 0;
+    return PyFloat_Pack4(x, p, PY_LITTLE_ENDIAN);
 }
 
 static int