From 68b8625e02254764eadeef7fcc721de74487fb39 Mon Sep 17 00:00:00 2001 From: PhysicistJohn <54456354+PhysicistJohn@users.noreply.github.com> Date: Tue, 11 Aug 2026 06:41:05 -0700 Subject: [PATCH] gh-154568: Fix array unpickling of little-endian float16 (GH-154569) Fix array._array_reconstructor() ignoring requested byte order for float16. The slow-path decoder for IEEE_754_FLOAT16_LE/BE computed the byte-order flag by comparing mformat_code against IEEE_754_FLOAT_LE (the 32-bit float constant) instead of IEEE_754_FLOAT16_LE. Since the float16 mformat codes are never equal to that constant, the comparison was always false, so the decoder always treated input as big-endian regardless of what was requested. Adds a regression test. (cherry picked from commit f3be08aa6f6f8fbb48b532dc75845be5b3c83a8b) Co-authored-by: PhysicistJohn <54456354+PhysicistJohn@users.noreply.github.com> --- Lib/test/test_array.py | 14 ++++++++++++++ ...00-00.gh-issue-154568.float16-reconstructor.rst | 1 + Modules/arraymodule.c | 2 +- 3 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2026-07-23-21-00-00.gh-issue-154568.float16-reconstructor.rst diff --git a/Lib/test/test_array.py b/Lib/test/test_array.py index 42213120fb6ced4..430f55f2129884b 100755 --- a/Lib/test/test_array.py +++ b/Lib/test/test_array.py @@ -224,6 +224,10 @@ def test_numbers(self): [-1<<63, (1<<63)-1, 0]), (['l'], SIGNED_INT64_BE, '>qqq', [-1<<63, (1<<63)-1, 0]), + (['e'], IEEE_754_FLOAT16_LE, 'eeee', + [1.0, float('inf'), float('-inf'), -0.0]), (['f'], IEEE_754_FLOAT_LE, 'ffff', @@ -254,6 +258,16 @@ def test_numbers(self): self.assertEqual(a, b, msg="{0!r} != {1!r}; testcase={2!r}".format(a, b, testcase)) + def test_float16_endianness(self): + # gh-154568: array_reconstructor() slow-path decoder for + # IEEE_754_FLOAT16_LE ignored the encoding. + le_bytes = struct.pack('e', 1.5) + b_le = array_reconstructor(array.array, 'd', IEEE_754_FLOAT16_LE, le_bytes) + b_be = array_reconstructor(array.array, 'd', IEEE_754_FLOAT16_BE, be_bytes) + self.assertEqual(b_le.tolist(), [1.5]) + self.assertEqual(b_be.tolist(), [1.5]) + def test_unicode(self): teststr = "Bonne Journ\xe9e \U0002030a\U00020347" testcases = ( diff --git a/Misc/NEWS.d/next/Library/2026-07-23-21-00-00.gh-issue-154568.float16-reconstructor.rst b/Misc/NEWS.d/next/Library/2026-07-23-21-00-00.gh-issue-154568.float16-reconstructor.rst new file mode 100644 index 000000000000000..85c0c81231b6aea --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-07-23-21-00-00.gh-issue-154568.float16-reconstructor.rst @@ -0,0 +1 @@ +Fix :mod:`array` unpickling of little-endian float16. diff --git a/Modules/arraymodule.c b/Modules/arraymodule.c index 46aec5f201c849b..45cdf351cbb6ce8 100644 --- a/Modules/arraymodule.c +++ b/Modules/arraymodule.c @@ -2356,7 +2356,7 @@ array__array_reconstructor_impl(PyObject *module, PyTypeObject *arraytype, case IEEE_754_FLOAT16_LE: case IEEE_754_FLOAT16_BE: { Py_ssize_t i; - int le = (mformat_code == IEEE_754_FLOAT_LE) ? 1 : 0; + int le = (mformat_code == IEEE_754_FLOAT16_LE) ? 1 : 0; Py_ssize_t itemcount = Py_SIZE(items) / 2; const char *memstr = PyBytes_AS_STRING(items);