I’m building python 3.14.7 from source using gcc 16.2.0 on Rocky 8. I have tried -O3, -O2, and -std=gnu17, but they all give the following error:
# Next, run the profile task to generate the profile information.
LD_PRELOAD=/tmp/uvacse/Python/3.14.7/GCC-16.2.0/Python-3.14.7/libpython3.14.so ./python -m test --pgo --timeout=
Using random seed: 463716094
...
test_small_n (test.test_float.RoundTestCase.test_small_n) ... ok
======================================================================
ERROR: test_float_pow (test.test_float.GeneralFloatCases.test_float_pow)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/uvacse/Python/3.14.7/GCC-16.2.0/Python-3.14.7/Lib/test/test_float.py", line 609, in test_float_pow
self.assertEqualAndEqualSign(pow_op(-2.0, -2000.0), 0.0)
~~~~~~^^^^^^^^^^^^^^^
OverflowError: (34, 'Numerical result out of range')
----------------------------------------------------------------------
Ran 54 tests in 0.439s
FAILED (errors=1, skipped=1)
test test_float failed
0:01:11 load avg: 7.72 [16/43] test_float failed (1 error)
(Edit: I just tried gcc 14.2.0 and it gave the same error.)
If I switch to -Ofast I get lots more test failures.
On the same computer I never encountered this for versions 3.13 and earlier (but I used gcc 14 and earlier).
I am seeing the same OverflowError in test_float_pow while building Python 3.14.7 from source, including with GCC 14.2.0 and the optimization levels you listed. It is especially puzzling because earlier Python versions built successfully on the same machine, so I am looking forward to understanding whether this is a compiler or 3.14.7 test interaction. Could you share the exact configure options and platform details so the cause can be narrowed down?
a little bit surprising — I also tried this in a Rocky 8 Docker container and still couldn’t reproduce it.
I used rockylinux:8 image and GCC 14.2.1 (installed via gcc-toolset), with the same configure options you posted.
LD_LIBRARY_PATH=. ./python -m test test_float -m test_float_pow -v
== CPython 3.14.7 (tags/v3.14.7:823f0323ee6, Aug 14 2026, 09:39:01) [GCC 14.2.1 20250110 (Red Hat 14.2.1-11)]
== Linux-6.8.0-63-generic-x86_64-with-glibc2.28 little-endian
== Python build: release shared LTO+PGO
== cwd: /root/cpython/build/test_python_worker_30036æ
== CPU count: 128
== encodings: locale=UTF-8 FS=utf-8
== resources: all test resources are disabled, use -u option to unskip tests
Using random seed: 3060026173
0:00:00 load avg: 1.65 Run 1 test sequentially in a single process
0:00:00 load avg: 1.65 [1/1] test_float
test_float_pow (test.test_float.GeneralFloatCases.test_float_pow) ... ok
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK
0:00:00 load avg: 1.65 [1/1] test_float passed
== Tests result: SUCCESS ==
Could you show what your build of Python produces at the interactive interpreter prompt for each of the following cases? I have a guess at what might be happening here, but the three cases below would help corroborate or refute that guess.
Python 3.14.7 (main, Aug 7 2026, 02:15:30) [Clang 22.1.3 ] on darwin
Type "help", "copyright", "credits" or "license" for more information.
Cmd click to launch VS Code Native REPL
>>> pow(-2.0, -2000.0)
0.0
>>> pow(2.0, -2000.0)
0.0
>>> pow(2.0, -20_000.0)
0.0
$ LD_LIBRARY_PATH=. ./python
Python 3.14.7 (main, Aug 13 2026, 08:03:18) [GCC 16.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> pow(-2.0, -2000.0)
0.0
>>> pow(2.0, -2000.0)
0.0
>>> pow(2.0, -20_000.0)
0.0
Separately, I just tried -O0 and it completed successfully.
Reproduces on RedHat 8.10 with software built by me and on Ubuntu 26.04 with OS packages:
CFLAGS="-fno-math-errno" ./configure --enable-optimizations
make -j
For the test you ask for - the first call to pow is successful and subsequent ones in the same session fail:
$ ./python
Python 3.14.7 (main, Aug 14 2026, 14:47:12) [GCC 15.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> pow(-2.0, -2000.0)
0.0
>>> pow(2.0, -2000.0)
Traceback (most recent call last):
File "<python-input-1>", line 1, in <module>
pow(2.0, -2000.0)
~~~^^^^^^^^^^^^^^
OverflowError: (34, 'Numerical result out of range')
>>> pow(2.0, -20_000.0)
Traceback (most recent call last):
File "<python-input-2>", line 1, in <module>
pow(2.0, -20_000.0)
~~~^^^^^^^^^^^^^^^^
OverflowError: (34, 'Numerical result out of range')
Reversing order:
$ ./python
Python 3.14.7 (main, Aug 14 2026, 14:47:12) [GCC 15.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> pow(2.0, -20_000.0)
0.0
>>> pow(-2.0, -2000.0)
Traceback (most recent call last):
File "<python-input-1>", line 1, in <module>
pow(-2.0, -2000.0)
~~~^^^^^^^^^^^^^^^
OverflowError: (34, 'Numerical result out of range')
Thanks, @branfosj! That -fno-math-errno is the smoking gun.
So my earlier undisclosed guess was completely wrong, so I’m not going to embarrass myself by disclosing it. Here’s a better guess:
all these pow cases underflow to zero
glibc’s pow is setting errno=ERANGE for this underflow-to-zero case (which it’s perfectly entitled to do - the C standard is very permissive when it comes to errno for math functions)
the -fno-math-errno flag gives gcc permission to assume that math function calls don’t set errno (and it may or may not make use of that assumption)
CPython has guard code that checks for errno=ERANGE in an underflow case and resets errno to 0
in the presence of -fno-math-errno, gcc is entitled to just assume that after the sequence here, errno is still zero (even though it isn’t), and so it skips the guard that would have reset errno to zero
so in the final block, errno is still ERANGE (because the guard failed), and Python goes on to raise OverflowError
Short version: CPython’s source here is incompatible with -fno-math-errno.
The weird “successful first time, fails on subsequent evaluations” behaviour appears to be due to an interaction with the PGO optimization, leading to two separate paths in the instrumented build, one of which is taken on the first call, and the other of which is taken on subsequent calls. That’s valid behaviour: given -fno-math-errno, gcc is free to either assume errno is zero or not on a whim; on the first path it actually performs the errno check; on the second it assumes (incorrectly) that errno is zero, because we told it it could.
So my assessment is: not a gcc bug, not a glibc bug, not a CPython bug. Possibly an EasyBuild bug (disclaimer: I have zero familiarity with EasyBuild). That said, I think the CPython code could potentially be hardened here to avoid the possibility of failure in the presence of -fno-math-errno. That’s easier than it used to be - back when we were supporting non-IEEE 754 platforms, we had to allow for the possibility that overflow would be indicated solely via errno. With today’s CPython assumption that we’re using IEEE 754 format, we can somewhat reasonably assume that any overflow will be accompanied by a +/-inf result, and we can do an isinf to check for that. I don’t know if there are other sites in the CPython source that would need similar hardening, though; it’s probably best to assume for now that CPython needs -fmath-errno.
@rsdmse You didn’t mention -fno-math-errno directly, but you did indicate you were using EasyBuild; I suspect that’s how -fno-math-errno is sneaking in, and it’s why you’re seeing the issue.
And it’s totally expected that -ffast-math and similar flags would cause lots of test failures; CPython does try to be well-behaved around IEEE 754 edge cases, and letting gcc play fast and loose with floating-point is incompatible with that.
I’ve narrowed down that this changed between 3.14.3 and 3.14.4. Based on the PGO optimisation comment, I narrowed this further to the addition of -fprofile-update=atomic.
Removing that from 3.14.4 (or 3.14.7) and I no longer see the issue; adding to to 3.14.3 and I do.
I am unsure what to make of that change in behaviour!
Thank you for the detailed explanation! I never thought it would be one of those “default” GCC flags set by EasyBuild so I left those out in my post. By setting 'precise': True (which effectively replaces -fno-math-errno with -mno-recip) the error went away.
Also thanks to everyone else for looking into this.
Me too! But I don’t think it’s all that useful to track down why that flag made a difference. Using -fno-math-errno gives gcc a piece of information that simply isn’t true - that flag tells gcc that libm calls don’t set errno (or at least that it’s safe to assume that libm calls don’t set errno), when in reality they do, and it isn’t safe to assume they don’t.
I think of the optimisation machinery as a highly complicated and capricious black box - it may or may not choose to make use of any particular piece of information it has, based on any number of variables (wind speed, time of day, the price of salmon in the local market). If you give it bad information, then it might not use the information, and then everything’s fine. Or it might use it, and then all bets are off.
So the fix is to avoid giving the false information in the first place.
Pinging @storchaka@tim.one@skirpichev: is it worth opening a GitHub issue for the idea of removing CPython’s dependence on errno here? It would be nice to free the code from reliance on “global” (really thread-local) state for what should be a pure function, for all sorts of reasons - easier to reason about, opportunities for optimization, etc.
float_pow in particular seems unnecessarily evil in that it’s not just reading that global state, but also writing to it (code ref) to force overflow exceptions, so even on boxes whose libm doesn’t touch errno (e.g., macOS), correct operation still relies on writes and reads of errno.
Getting rid of the errno dependence wasn’t really an option before we assumed IEEE 754 for CPython, but I believe it’s now reasonable.
I don’t have resources to work on this myself, so I don’t want to spam the issue tracker unless there’s interest from others. But if there is interest, I’ll happily open that issue.
You really meant just a given particular case or a more systematic approach, i.e. removing that everywhere it’s possible? Just for powers (_Py_ADJUST_ERANGE* only used by tp_power slots)?
Or math_1() helper, for instance. See also the recent issue 153144, where ignoring platforms errno helps.
off-topic
Really? Floating-point environment has a rounding mode. That can be changed, in principle.
we don’t want to close the door on porting CPython to a non-IEEE 754 platform
Though, the given case doesn’t impose any API constraints, that will require deprecations for such porting. So, someone willing to port CPython on non-IEEE box — can eventually revert proposed changes and restore errno-dependencies.
In that sense, this looks as a good proposal for me. Moreover, it seems that the cmath module already assume IEEE box: except for few cases, libm’s errno is ignored here.
I can’t promise that I’ll work on this, but such issue wouldn’t look like a spam for me.
I’m not sure what scope you have it mind or how aggressively to remove errno. I quickly played around with some code: Remove `_Py_ADJUST_ERANGE*()` functions. by hpkfft · Pull Request #1 · hpkfft/cpython · GitHub
Feel free to comment there if you wish. If this code (perhaps with modifications) is appropriate for the issue you suggest opening, I’d be happy to make PR upstream.