Bug description:
array._array_reconstructor() (the internal function used to unpickle
array.array objects) ignores the requested byte order when decoding
half-precision floats. The slow-path decoder for
IEEE_754_FLOAT16_LE/IEEE_754_FLOAT16_BE (Modules/arraymodule.c)
computes the byte-order flag by comparing mformat_code against
IEEE_754_FLOAT_LE (the 32-bit float constant, value 14) instead of
IEEE_754_FLOAT16_LE (value 26). Since the float16 mformat codes are
26/27, this comparison is always false, so the decoder always treats
the input as big-endian, regardless of which byte order was actually
requested.
Reproducer (using typecode 'd' to deterministically force the slow
/converting decode path regardless of the test machine's native
endianness):
import array, struct
le_bytes = struct.pack('<e', 1.0)
result = array._array_reconstructor(array.array, 'd', 26, le_bytes) # 26 = IEEE_754_FLOAT16_LE
print(result.tolist()) # [3.5762786865234375e-06] -- should be [1.0]
The sibling cases in the same switch statement (IEEE_754_FLOAT_LE/BE,
IEEE_754_DOUBLE_LE/BE, the complex variants) all correctly compare
mformat_code against their own constant; the float16 case is the only
one comparing against the wrong constant.
Real-world impact: pickling an array('e', ...) (half-precision float
array) on one machine and unpickling it on a machine with different
native endianness silently produces wrong values instead of either the
correct values or an error.
CPython versions tested on:
CPython main branch (3.16.0a0)
Operating systems tested on:
macOS (arm64)
Linked PRs
Bug description:
array._array_reconstructor()(the internal function used to unpicklearray.arrayobjects) ignores the requested byte order when decodinghalf-precision floats. The slow-path decoder for
IEEE_754_FLOAT16_LE/IEEE_754_FLOAT16_BE(Modules/arraymodule.c)computes the byte-order flag by comparing
mformat_codeagainstIEEE_754_FLOAT_LE(the 32-bit float constant, value 14) instead ofIEEE_754_FLOAT16_LE(value 26). Since the float16 mformat codes are26/27, this comparison is always false, so the decoder always treats
the input as big-endian, regardless of which byte order was actually
requested.
Reproducer (using typecode 'd' to deterministically force the slow
/converting decode path regardless of the test machine's native
endianness):
The sibling cases in the same switch statement (
IEEE_754_FLOAT_LE/BE,IEEE_754_DOUBLE_LE/BE, the complex variants) all correctly comparemformat_codeagainst their own constant; the float16 case is the onlyone comparing against the wrong constant.
Real-world impact: pickling an
array('e', ...)(half-precision floatarray) on one machine and unpickling it on a machine with different
native endianness silently produces wrong values instead of either the
correct values or an error.
CPython versions tested on:
CPython main branch (3.16.0a0)
Operating systems tested on:
macOS (arm64)
Linked PRs