Repository navigation
Remove "unknown_format" code path in PyFloat_Pack/Unpack*() #145633
Description
This is a follow-up of the #91073. Proposal was briefly discussed in the referenced issue.
Now support for IEEE floating-point formats is a requirement and the configure script will fail, if it can't detect it. The only exception is some ARM platforms (this come from b08a53a):
Lines 6172 to 6180 in 149c465
| [*arm*], [# Some ARM platforms use a mixed-endian representation for | |
| # doubles. While Python doesn't currently have full support | |
| # for these platforms (see e.g., issue 1762561), we can at | |
| # least make sure that float <-> string conversions work. | |
| # FLOAT_WORDS_BIGENDIAN doesn't actually detect this case, | |
| # but if it's not big or little, then it must be this? | |
| AC_DEFINE([DOUBLE_IS_ARM_MIXED_ENDIAN_IEEE754], [1], | |
| [Define if C doubles are 64-bit IEEE 754 binary format, | |
| stored in ARM mixed-endian order (byte order 45670123)])], |
This is only case, when "unknown_format" code patch now could be triggered (BTW, we could detect this format in runtime and switch to yet another "just copy bytes" path). Should we keep this for an unsupported platform? IIRIC, such chips aren't supported even in Debian.
With "unknown_format" we also could drop code to detect endianness in runtime. I'm not sure about float.__getformat__ function. It's docstring says:
>>> help(float.__getformat__)
Help on built-in function __getformat__:
__getformat__(typestr, /) class method of builtins.float
You probably don't want to use this function.
typestr
Must be 'double' or 'float'.
It exists mainly to be used in Python's test suite.
This function returns whichever of 'unknown', 'IEEE, big-endian' or 'IEEE,
little-endian' best describes the format of floating-point numbers used by the
C type named by typestr.
Probably, it could be treated as private and we should remove it in favor of using configure macros in tests. (__set_format__() method was removed without prior deprecation in 5ab745f). Edit: we could also leave this also as-is for alternative Python implementations, using CPython tests.
Linked PRs
- gh-145633: remove support for ancient ARM platforms with mixed-endian doubles #145634
- gh-145633: Refine notes on non-IEEE platforms #145845
- gh-145633: drop runtime checks for floatting-point formats in the array module #146162
- gh-145633: remove remnants of the "unknown_format" (floats) in the struct module #146189
- gh-85989: deprecate float.__getformat__() class method #146400
- gh-145633: Fix struct.pack('f') on s390x #146422
- [3.14] gh-145633: Fix struct.pack('f') on s390x (GH-146422) #146460
- [3.13] gh-145633: Fix struct.pack('f') on s390x (#146422) #146461
- gh-145633: Allow PyFloat_Pack8 & PyFloat_UnPack* to fail in future CPython versions #153440
- [3.15] gh-145633: Allow PyFloat_Pack8 & PyFloat_UnPack* to fail in future CPython versions (GH-153440) #153490
Activity
vstinner commentedon Mar 9, 2026
Yes, that would be a safe idea. For now, I prefer to wait until pending review comments are addressed.
gh-145633: Remove support for ancient ARM platforms with mixed-endian…
vstinner commentedon Mar 11, 2026
I don't close the issue now, since @skirpichev still plans to remove DOUBLE_IS_BIG_ENDIAN_IEEE754 and DOUBLE_IS_LITTLE_ENDIAN_IEEE754 macros.
skirpichev commentedon Mar 12, 2026
My point is that usually (always?) endianness for floats will match endianness for other types. We could check that in the configure script and fail if this is not true. Though, it's not hard to support the current way, just for some unknown, odd, future (?) system.
But maybe we should drop
__getformat__()method?It's really used in tests to get the requires_IEEE_754 decorator. But I think it used incorrectly in many cases. For instance, take look on the testCopysign() in the test_math.py. This case has nothing to do with the format of floating-point numbers, but rather we expect here more strict conformance to the IEC 60559, i.e. that arithmetic operations strictly follow to the standard semantics, we assume some behavior of functions. In this case, expected certain behavior from the libm's copysign().
This is the case for the
sys.float_info.iec_60559-like flag. I think it's better to use something like this to implement the requires_IEEE_754 decorator. In few cases (something like pack/unpack tests) it will be overkill and we really need just__getformat__(). But I think it's better to assume, that pack/unpack functions will just work with IEEE formats (I'll update docs to mention this). Alternative implementations may use some tricks to make it works, like we did before.pythongh-145633: amend dae85c4 (refine notes on non-IEEE platforms)
pythongh-145633: amend dae85c4 (refine notes on non-IEEE platforms)
vstinner commentedon Mar 12, 2026
It's used by a few projects in PyPI top 15,000 projects:
It may be safer to deprecate the method first if we plan to remove it.
skirpichev commentedon Mar 13, 2026
Though, it seems in some cases they just clone CPython tests.
numpy.float64 is compatible with builtin float, hence this method.
Do you think it's ok to do now or we should wait fate of #138580 ?
vstinner commentedon Mar 13, 2026
I'm ok with deprecating
float.__getformat__()right now.pythongh-145633: drop runtime checks for floatting-point format in th…
gh-145633: Refine notes on non-IEEE platforms (#145845)
pythongh-145633: remove remnants of the "unknown_format" (floats) in …
skirpichev commentedon Mar 25, 2026
Then we need a replacement for this to implement the
support.requires_IEEE_754decorator. It seems, that thesys.float_info.iec_60559is not a good option for this.We can either keep things as is, using
__getformat__()to implement the decorator. No deprecations.OR change
iec_60559flag meaning to be true, if the float type characteristics matches the IEC 60559 double format (regardless on__STDC_IEC_559__value). And then - use this flag in tests, deprecate__getformat__().OR just use some runtime checks to verify floating-point format. I think that checking sys.float_info parameters wrt the IEC double format could work in 99% cases, but we could do also direct tests like we did in the
__getformat__()before, usingctypesmodule. And deprecate__getformat__().I'm going to implement the last variant.
pythongh-145633: deprecate float.__getformat__() class method
gh-145633: Drop runtime checks for floatting-point formats in the arr…
gh-145633: Remove remnants of the "unknown_format" (floats) in the st…
encukou commentedon Mar 25, 2026
The s390x buildbots are broken, see e.g. here.
It looks like it was caused by these changes.
skirpichev commentedon Mar 25, 2026
Hmm, failures start from https://buildbot.python.org/#/builders/1565/builds/4397
I guess, it might be only #146189.
vstinner commentedon Mar 25, 2026
s390x is the only big endian (
sys.byteorder: big) buildbot. So maybetest_structneeds to be updated?skirpichev commentedon Mar 25, 2026
I don't think so.
Looks the change reveal a bug:
The problem, that native conversion does type cast (double->float), but no checks for overflows, we have correctly packed infinity:
cpython/Modules/_struct.c
Lines 763 to 774 in 4240c7d
c.f. PyFloat_Pack4():
cpython/Objects/floatobject.c
Lines 2015 to 2019 in 4240c7d
I can prepare a patch tomorrow. Else, I could suggest quickly revert #146189.
encukou commentedon Mar 25, 2026
Thank you!
It's a Tier-3 platform; tomorrow is fine.
vstinner commentedon Mar 25, 2026
Oh, @skirpichev already posted a more complete analysis while I was analyzing the bug. I post my notes anyway.
The failing test is:
Before, it raised OverflowError. Now, it does not raise any exception (and so the test fails).
With the change,
bigendian_tableis updated to use native functions:So
struct.pack(">f", big)callsnp_float()which does:Before,
bp_float()was called, it does:vstinner commentedon Mar 25, 2026
@skirpichev: What do you think of this fix? We might have to extend test_struct to test more cases (test the 3 endian: big, little, native).
skirpichev commentedon Mar 25, 2026
Looks good. Please make a pr if you have a free time.
This will also fix discrepancy in packing of nans on some platforms.
Lets test also half-floats here. Just in case, they might be affected in principle.
pythongh-145633: Fix struct.pack('f') on s390x
pythongh-145633: Fix struct.pack('f') on s390x
vstinner commentedon Mar 25, 2026
I wrote #146422 to fix the issue and add more tests.
gh-145633: Fix struct.pack('f') on s390x (#146422)
pythongh-145633: Fix struct.pack('f') on s390x (pythonGH-146422)
[3.14] gh-145633: Fix struct.pack('f') on s390x (GH-146422) (#146460)
[3.13] gh-145633: Fix struct.pack('f') on s390x (#146422) (#146461)
encukou commentedon Apr 16, 2026
The PR added this to
PyFloat_Pack8:I think this is unfortunate, as it's a promise for future versions of CPython -- it essentially means CPython will never again be portable to non-IEEE-double platforms. That's a much stronger promise that the current version of Python requiring IEEE floating point.
IMO, this should be an internal implementation detail.
vstinner commentedon Apr 16, 2026
Require IEEE 754 floating point numbers was a deliberate choice made in Python 3.11 to simplify the Python code (drop legacy code for non-IEEE platforms). New platforms are compatible with IEEE 754 which is a de facto standard. Only very old unmaintained plaforms don't support IEEE 754.
pythongh-145633: Drop runtime checks for floatting-point formats in t…
pythongh-145633: Remove remnants of the "unknown_format" (floats) in …
pythongh-145633: Fix struct.pack('f') on s390x (python#146422)
encukou commentedon Apr 17, 2026
I agree with dropping untested code, but I don't think we should prevent porting CPython to old (or future) non-IEEE platforms.
skirpichev commentedon Apr 17, 2026
The only way to do this is to revert choice made. Thread: https://mail.python.org/archives/list/python-dev@python.org/thread/J5FSP6J4EITPY5C2UJI7HSL2GQCTCUWN/
I would say it will be a step backward.
pythongh-145633: Remove support for ancient ARM platforms with mixed-…
pythongh-145633: Refine notes on non-IEEE platforms (python#145845)
pythongh-145633: Drop runtime checks for floatting-point formats in t…
pythongh-145633: Remove remnants of the "unknown_format" (floats) in …
pythongh-145633: Fix struct.pack('f') on s390x (python#146422)
skirpichev commentedon May 4, 2026
https://discuss.python.org/t/deprecate-float-getformat/107151 - discussion of
__getformat__removal.Lets close this. If someone wish to continue - it would be better to open a new issue.
encukou commentedon Jun 15, 2026
I opened one in the C API WG repo: capi-workgroup/decisions#107
gh-145633: Allow PyFloat_Pack8 & PyFloat_UnPack* to fail in future CP…
[3.15] gh-145633: Allow PyFloat_Pack8 & PyFloat_UnPack* to fail in fu…