You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[NetBSD] float formatting change between Python 3.11 and 3.12? #128005
While looking at a ntpsec test failure, I looked at the Python self tests and noticed that some are failing on NetBSD 10/amd64.
I extraced and minimized one test case based on test_format:
#!/usr/bin/env pythondeftestformat(formatstr, args, output=None, limit=None):
result=formatstr%argsifoutputandlimitisNoneandresult!=output:
raiseAssertionError("%r %% %r == %r != %r"%
(formatstr, args, result, output))
# when 'limit' is specified, it determines how many characters# must match exactly; lengths must always match.# ex: limit=5, '12345678' matches '12345___'# (mainly for floating-point format tests for which an exact match# can't be guaranteed due to rounding and representation errors)elifoutputandlimitisnotNoneand (
len(result) !=len(output) orresult[:limit] !=output[:limit]):
print("%s %% %s == %s != %s"%
(repr(formatstr), repr(args), repr(result), repr(output)))
testformat(b"%a", (3.14,), b"3.14")
This test fails with Python 3.13.1 and Python 3.12.8:
So basically the %a format specifier outputs the full precision, which is why you're getting b'3.1400000000000001' instead of b'3.14' and you're calling testformat(b"%a", (3.14,), b"3.14"), which formats the floating-point number 3.14 using %a in bytes format.
So I THINK the assertion fails because the result of the format (b'3.1400000000000001') does not match the expected output (b'3.14').
The floating-point representation format with %a is too precise even when you round it, even though you might expect it to show only 3.14 (as it would with %f)..... Unless you need the hexadecimal outputs, you gotta use round() to limit the precision before formatting, or obv just adjust the formatting you have according to what you want output. If you're trying to format floating-point numbers with decimal places, use %f instead of %a.
Bottom-line: Try removing the %a! (You've prob tried this - but if I can some more context I would love to help)
Gary Miller here, I am working this issue from the NTPSec side. We are unable to reproduce. The test case presented here is an abstraction of some long standing NTPSec tests that are known to pass on every arch we can test on. Except for the OPs.
Looks like ordinary IEEE doubles. If it's info not from the OP's system, where this issue happens, hardly it's relevant.
%a is too precise even when you round it, even though you might expect it to show only 3.14 (as it would with %f)..... Unless you need the hexadecimal outputs
Python doesn't support hexadecimal output via old-style formatting nor with format() or f-strings. "%a" means string conversion with ascii():
So it seems that you're possibly dealing with an issue related to floating-point formatting in Python 3.13.1, which might be affecting your NTPSec-side code. Specifically, the default floating-point formatting behavior (sys.float_repr_style = 'short') and the fact that floating-point numbers in Python use double precision which can lead to issues with how numbers are displayed or handled.
From what I gathered: Python 3.13.1 uses double-precision floating-point numbers as standard (sys.float_info), which means numbers like 3.14 may not be represented exactly as 3.14 but as something like 3.1400000000000001. This can cause issues if you're expecting exact decimal representation in your output or in calculations.
Additionally, Python's default short format (sys.float_repr_style = 'short') provides a compact, potentially scientific-notation based string representation for floating-point numbers, which could lead to discrepancies in output formatting.
If the NTPSec code (or your code interacting with it) is producing inconsistent results with floating-point numbers, you can try rounding the numbers like I said, or use the decimal.Decimal class? So for example:
Let’s assume that the NTPSec code (or your code that interacts with NTPSec) is having issues formatting floats or handling them in a consistent way (which I think is what you're getting at?)
To ensure compatibility with Python 3.13.1 and avoid floating-point inaccuracies:
import sys
from decimal import Decimal
Step 1: Round floats to avoid precision issues
value = 3.1400000000000001
rounded_value = round(value, 2)
Step 2: Alternatively, use Decimal for precise decimal handling
value_decimal = Decimal('3.14')
Step 3: Format for output
formatted_value = "{:.2f}".format(rounded_value) # or use "%.2f" % rounded_value
print(formatted_value) # Output: 3.14
For scientific notation handling, you can also use:
Looks like ordinary IEEE doubles. If it's info not from the OP's system, where this issue happens, hardly it's relevant.
I mention it because some people, elsewhere, have thought this a common bug, just trying to narrow it back down again. It is not just "3.11 and 3.12", it is only a very specific 3.12 that files.
The %a is a read herring. The bug NTPsec is seeing is in formatting %.2f. When the test does:
Looks like ordinary IEEE doubles. If it's info not from the OP's system, where this issue happens, hardly it's relevant.
%a is too precise even when you round it, even though you might expect it to show only 3.14 (as it would with %f)..... Unless you need the hexadecimal outputs
Python doesn't support hexadecimal output via old-style formatting nor with format() or f-strings. "%a" means string conversion with ascii():
>>> "%a" % 3.14
'3.14'
>>> ascii(3.14)
'3.14'
It's same as str/repr for floats.
%a converts the value to a string using the repr() function, which is meant to produce a string representation of the object that can (ideally) be used to recreate the object. For floating-point numbers, it will output the number in hexadecimal floating-point notation..... No, %a does not refer to string conversion using ascii(). Instead, %a is the format specifier for repr() in Python?
num = 3.14
print("%a" % num)
'3.1400000000000001'
This shows the repr() of 3.14 with full precision. If you use f-strings, it’s equivalent to calling repr():
<< print(f"{num!a}")
'3.1400000000000001'
I could be looking at old current documentation only applicable to 3.13.1 (I'm going to review the documentation you sent right now)
So it seems that you're possibly dealing with an issue related to floating-point formatting in Python 3.13.1,
Don't fall into the generalization trap. The vast majority of 3.13.1 passes the NTPSec tests just fine. Just this one particular NETBSD has shown the issue.
From what I gathered: Python 3.13.1 uses double-precision floating-point numbers as standard (sys.float_info), which means numbers like 3.14 may not be represented exactly as 3.14 but as something like 3.1400000000000001.
Except the "%.2f" is supposed to force only 2 digits past the decimal.
Please forget "%a" was ever mentioned. The original problem is with "%.2f"
Okay, fully understood now (to the extent I can see from my side at least lol)... and now I'm at a slight loss... last 3 recommendations:
To control the precision or format of floating-point numbers, use string formatting (f"{num}", f"{num:.2f}", f"{num:.2e}", or f"{num!a}") rather than sys.float_repr_style.
Round the number BEFORE formatting (using round())
Use Decimal type to avoid floating-point precision issues?
import sys
from decimal import Decimal
num = Decimal('12.34')
print(f"{num:.2f}")
'12.34'
Except you don't have this issue on other versions...... what the heck
I mean if you set sys.float_repr_style = 'legacy', it would effect the string representation of floats when printing.... allowing it HOPEFULLY return to normal? just not actually fix your underlying floating-point precision issue....
Okay I reviewed the original file and ran it:
To support Python 3.13.1 and fix the floating-point formatting issues in NTPsec, you can:
Round explicitly using round(num, 2) before formatting the number with %.2f.
Use decimal.Decimal if you need more precise control over floating-point operations (especially when dealing with time or monetary values).
**Adjust the test tolerance to account for small floating-point errors if those differences are acceptable.
**Refactor your code to ensure consistency in rounding and formatting.
For example, in your test_summary test case, you could adjust the formatting as follows:
The failures are related to the rounding behavior of floating-point numbers, specifically:
Test test_summary: The failure is because Python is formatting 1.2345999999999999 as 1.2346 (but the expected output is 1.2346).
Test test_rfc3339: The failure is due to Python representing 1480999786.025 as 2016-12-06T04:49:46.0250001Z instead of the expected 2016-12-06T04:49:46.025Z.
Test test_stringfiltcooker: Similar floating-point precision issues are causing it to represent numbers slightly differently (0.00068000000000000005 vs. 0.00068).
Test test_unitify: The failure is due to the number being rounded to 12.34ppm instead of 12.35ppm.
changed the title [-]float formatting change between Python 3.11 and 3.12?[/-][+][NetBSD] float formatting change between Python 3.11 and 3.12?[/+]on Dec 17, 2024
skip these tests (maybe the whole file) for non-IEEE systems
I built Python with "legacy" formatting:
diff --git a/Include/internal/pycore_pymath.h b/Include/internal/pycore_pymath.h
index eea8996ba68..c64e5d87932 100644
--- a/Include/internal/pycore_pymath.h+++ b/Include/internal/pycore_pymath.h@@ -181,11 +181,7 @@ extern void _Py_set_387controlword(unsigned short);
// (2) we're on x86 with the rounding precision set to 64-bits
// (extended precision), and we don't know how to change
// the rounding precision.
-#if !defined(DOUBLE_IS_LITTLE_ENDIAN_IEEE754) && \- !defined(DOUBLE_IS_BIG_ENDIAN_IEEE754) && \- !defined(DOUBLE_IS_ARM_MIXED_ENDIAN_IEEE754)-# define _PY_SHORT_FLOAT_REPR 0-#endif+#define _PY_SHORT_FLOAT_REPR 0
// Double rounding is symptomatic of use of extended precision on x86.
// If we're seeing double rounding, and we don't have any mechanism available
Is this a bug in strtod() on NetBSD, or just an implementation difference?
I think it's just the difference between short float repr and legacy float repr. I don't think there's any bug here, except possibly @skirpichev's point about cleaning up the tests to work with legacy float repr. And if we do that, it should be a separate issue, so I'm going to close this.
Activity
ericvsmith commentedon Dec 17, 2024
What are the values of
sys.float_infoandsys.float_repr_styleon the various Pythons you're testing?alliesw commentedon Dec 17, 2024
So basically the %a format specifier outputs the full precision, which is why you're getting b'3.1400000000000001' instead of b'3.14' and you're calling testformat(b"%a", (3.14,), b"3.14"), which formats the floating-point number 3.14 using %a in bytes format.
So I THINK the assertion fails because the result of the format (b'3.1400000000000001') does not match the expected output (b'3.14').
The floating-point representation format with %a is too precise even when you round it, even though you might expect it to show only 3.14 (as it would with %f)..... Unless you need the hexadecimal outputs, you gotta use round() to limit the precision before formatting, or obv just adjust the formatting you have according to what you want output. If you're trying to format floating-point numbers with decimal places, use %f instead of %a.
Bottom-line: Try removing the %a! (You've prob tried this - but if I can some more context I would love to help)
garyemiller commentedon Dec 17, 2024
Gary Miller here, I am working this issue from the NTPSec side. We are unable to reproduce. The test case presented here is an abstraction of some long standing NTPSec tests that are known to pass on every arch we can test on. Except for the OPs.
On Gentoo amd64, python 3.13.1t:
Similar Python 3.10.14 and up. For me.
skirpichev commentedon Dec 17, 2024
Looks like ordinary IEEE doubles. If it's info not from the OP's system, where this issue happens, hardly it's relevant.
Python doesn't support hexadecimal output via old-style formatting nor with format() or f-strings. "%a" means string conversion with ascii():
It's same as str/repr for floats.
alliesw commentedon Dec 17, 2024
So it seems that you're possibly dealing with an issue related to floating-point formatting in Python 3.13.1, which might be affecting your NTPSec-side code. Specifically, the default floating-point formatting behavior (sys.float_repr_style = 'short') and the fact that floating-point numbers in Python use double precision which can lead to issues with how numbers are displayed or handled.
From what I gathered: Python 3.13.1 uses double-precision floating-point numbers as standard (sys.float_info), which means numbers like 3.14 may not be represented exactly as 3.14 but as something like 3.1400000000000001. This can cause issues if you're expecting exact decimal representation in your output or in calculations.
Additionally, Python's default short format (sys.float_repr_style = 'short') provides a compact, potentially scientific-notation based string representation for floating-point numbers, which could lead to discrepancies in output formatting.
If the NTPSec code (or your code interacting with it) is producing inconsistent results with floating-point numbers, you can try rounding the numbers like I said, or use the decimal.Decimal class? So for example:
Let’s assume that the NTPSec code (or your code that interacts with NTPSec) is having issues formatting floats or handling them in a consistent way (which I think is what you're getting at?)
To ensure compatibility with Python 3.13.1 and avoid floating-point inaccuracies:
import sys
from decimal import Decimal
Step 1: Round floats to avoid precision issues
value = 3.1400000000000001
rounded_value = round(value, 2)
Step 2: Alternatively, use Decimal for precise decimal handling
value_decimal = Decimal('3.14')
Step 3: Format for output
formatted_value = "{:.2f}".format(rounded_value) # or use "%.2f" % rounded_value
print(formatted_value) # Output: 3.14
For scientific notation handling, you can also use:
scientific_value = "{:e}".format(3.1400000000000001)
print(scientific_value) # Output: 3.140000e+00
garyemiller commentedon Dec 17, 2024
I mention it because some people, elsewhere, have thought this a common bug, just trying to narrow it back down again. It is not just "3.11 and 3.12", it is only a very specific 3.12 that files.
The %a is a read herring. The bug NTPsec is seeing is in formatting %.2f. When the test does:
"%.2f" % 12.34
Instead of "12.34", we get "12.345999999999999"
alliesw commentedon Dec 17, 2024
%a converts the value to a string using the repr() function, which is meant to produce a string representation of the object that can (ideally) be used to recreate the object. For floating-point numbers, it will output the number in hexadecimal floating-point notation..... No, %a does not refer to string conversion using ascii(). Instead, %a is the format specifier for repr() in Python?
garyemiller commentedon Dec 17, 2024
Don't fall into the generalization trap. The vast majority of 3.13.1 passes the NTPSec tests just fine. Just this one particular NETBSD has shown the issue.
Except the "%.2f" is supposed to force only 2 digits past the decimal.
NTPSec already uses your "Step 3".
garyemiller commentedon Dec 17, 2024
Please forget "%a" was ever mentioned. The original problem is with "%.2f"
alliesw commentedon Dec 17, 2024
Okay, fully understood now (to the extent I can see from my side at least lol)... and now I'm at a slight loss... last 3 recommendations:
To control the precision or format of floating-point numbers, use string formatting (f"{num}", f"{num:.2f}", f"{num:.2e}", or f"{num!a}") rather than sys.float_repr_style.
Round the number BEFORE formatting (using round())
Use Decimal type to avoid floating-point precision issues?
Except you don't have this issue on other versions...... what the heck
alliesw commentedon Dec 17, 2024
I mean if you set sys.float_repr_style = 'legacy', it would effect the string representation of floats when printing.... allowing it HOPEFULLY return to normal? just not actually fix your underlying floating-point precision issue....
garyemiller commentedon Dec 17, 2024
NTPSec works on all Python versions from 2.6 to the latest.
Since 2 should solve the problem, but does not, the problem is prolly elsewhere.
I think we are on the same page now. Just need the OP to fix his test case, and post the requested sys.float_info and sys.float_repr_style.
UNtil I can duplicate it, I can't code a work around.
alliesw commentedon Dec 17, 2024
Okay I reviewed the original file and ran it:
To support Python 3.13.1 and fix the floating-point formatting issues in NTPsec, you can:
**Adjust the test tolerance to account for small floating-point errors if those differences are acceptable.
**Refactor your code to ensure consistency in rounding and formatting.
For example, in your test_summary test case, you could adjust the formatting as follows:
Assuming
datais the value that needs formattingRound and format the value with Decimal
Now you can format it like this
Expected output is '1.2346', now you can assert:
alliesw commentedon Dec 17, 2024
The failures are related to the rounding behavior of floating-point numbers, specifically:
Test test_summary: The failure is because Python is formatting 1.2345999999999999 as 1.2346 (but the expected output is 1.2346).
Test test_rfc3339: The failure is due to Python representing 1480999786.025 as 2016-12-06T04:49:46.0250001Z instead of the expected 2016-12-06T04:49:46.025Z.
Test test_stringfiltcooker: Similar floating-point precision issues are causing it to represent numbers slightly differently (0.00068000000000000005 vs. 0.00068).
Test test_unitify: The failure is due to the number being rounded to 12.34ppm instead of 12.35ppm.
10 remaining items
skirpichev commentedon Dec 17, 2024
IIUC, the test was extracted from test_format.py:
cpython/Lib/test/test_format.py
Line 345 in cfeaa99
It's coming from test_bytes_and_bytearray_format(), which it's not prefixed with something like
So, it will fail. But I suspect many other tests from that file will fail as well. For example:
cpython/Lib/test/test_format.py
Line 406 in cfeaa99
We have to decide:
Edit:
BTW, it looks we don't have any build bots, that works with
#define _PY_SHORT_FLOAT_REPR 0, i.e. ("legacy" repr for floats).[-]float formatting change between Python 3.11 and 3.12?[/-][+][NetBSD] float formatting change between Python 3.11 and 3.12?[/+]vstinner commentedon Dec 17, 2024
So it was a bug in NetBSD package? A NetBSD patch caused
sys.float_repr_styleto be"legacy"?vstinner commentedon Dec 17, 2024
All PEP 11 platforms support the floatting point "short" format.
vstinner commentedon Dec 17, 2024
I built Python with "legacy" formatting:
Only 2 tests of test_format are failing:
0-wiz-0 commentedon Dec 17, 2024
A pkgsrc patch, yes, the chunk starting here wasn't updated when the compile check was changed to a link check:
https://github.com/NetBSD/pkgsrc/blob/f1f5ea281ff130a99da13a0e91659e4018798283/lang/python313/patches/patch-configure#L37
0-wiz-0 commentedon Dec 17, 2024
The change for legacy -> short also fixed this problem for me:
and this one:
and this one:
and this one:
and these:
and this one:
and this one:
and these:
and some more, tired of copying & pasting.
Is this a bug in
strtod()on NetBSD, or just an implementation difference?ericvsmith commentedon Dec 17, 2024
I think it's just the difference between short float repr and legacy float repr. I don't think there's any bug here, except possibly @skirpichev's point about cleaning up the tests to work with legacy float repr. And if we do that, it should be a separate issue, so I'm going to close this.
skirpichev commentedon Dec 18, 2024
See #128051.
NetBSD: Switch to latest Python
NetBSD: Switch to latest Python