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
BUG: Highly degraded precision for tanh for arrays of float16 on Sapphire Rapids - Regression in 2.3.0 #30821
I ran into an issue I traced to numpy, that was introduced in 2.3.0, 2.2.6 works fine.
I only observed it on Intel Sapphire Rapids, but not AMD Zen3 CPUs
Calculating tanh of Float16 values has a large error when processing arrays. Most notably it saturates at 0.9985 instead of 1
How does this issue affect you or how did you find it:
E.g. the mish operation for ML/AI relies on tanh, specifically: x * tanh(softmax(x)). With this issue results are wildly off (due to multiplication with x amplifying the error)
Found in the PyTorch test suite where numpy is used as the reference
Can that be solved? Especially the saturation at less than 1 might cause issues with some applications, e.g. ML stuff could expect tanh(x) == 1 for large x, although, of course, it is mathematically only a limit
We probably have to have a discussion whether 4ULP is sensible here. There was a bit of an argument always that for these low precision types the users tend to want a fast tradeoff, but to some degrees I think these opinions on speed vs. precision don't have a super good foundation.
(Of course it is very hard to actually put them on a good foundation.)
Beyond ULP as you note there is also always the problem that certain behaviors can be very surprising. We had the issue with 64bit sin/cos which were probably precise enough but code failed because it wasn't monotonous enough (1 or 2 ULP jitters where the numerical value was ~1)...
If we decide against 4ULP for lower precision fps too, then there may still be a path to enable "fast but low precision". I am a bit wary about these low precision versions in NumPy, but I think there are also strong opinions that NumPy should not sacrifice speed too much.
Do we have a higher precision fp16 version and know the speed trade-off?
I'm planning to put together a detailed benchmark and report comparing different fp16/bf16 math implementations. I may open a repo and link it here.
In the meantime, this is worth reading: https://www.pythontutorials.net/blog/float16-is-much-slower-than-float32-and-float64-in-numpy/
As that article shows, the speed argument for fp16 is already compromised at the instruction level on CPUs, the emulation overhead from converting to/from fp32 erases any throughput advantage. Given that, there's little remaining justification for sacrificing precision in fp16 math functions. If we're not actually getting the speed, we may as well get the accuracy.
I doubt that is relevant. These changes here are precisely because float16 isn't just done in float32 anymore in practice which was the case before this change.
Do we have a higher precision fp16 version and know the speed trade-off?
It doesn’t look like it. The repository only includes the 4‑ULP variants of the float16 functions: https://github.com/numpy/SVML/tree/main/linux/avx512. I’m no longer at Intel, but I can try reaching out to a few folks to see whether the 1‑ULP versions can be open‑sourced. If it happens, it may take some time before they make it upstream.
In the meantime, should we consider reverting the current tanh implementation?
Activity
seberg commentedon Feb 11, 2026
CC @sterrettm2, @Mousius.
BiradarSiddhant02 commentedon Feb 18, 2026
An issue in pytorch seems to be similar.
pytorch/pytorch#173757
The above linked issue is more or a performance issue but worth looking at.
r-devulap commentedon Feb 18, 2026
This is coming from the 4 ULP SVML implementation of FP16
tanhcoming from https://github.com/numpy/SVML/blob/main/linux/avx512/svml_z0_atanh_h_la.s which only works on SPR.0.9985 and 1 are 3 ULP away, so this is within the 4 ULP error range.
BiradarSiddhant02 commentedon Feb 18, 2026
Ah ok makes sense!
Flamefire commentedon Feb 19, 2026
Can that be solved? Especially the saturation at less than 1 might cause issues with some applications, e.g. ML stuff could expect
tanh(x) == 1for largex, although, of course, it is mathematically only a limitseberg commentedon Feb 19, 2026
We probably have to have a discussion whether 4ULP is sensible here. There was a bit of an argument always that for these low precision types the users tend to want a fast tradeoff, but to some degrees I think these opinions on speed vs. precision don't have a super good foundation.
(Of course it is very hard to actually put them on a good foundation.)
Beyond ULP as you note there is also always the problem that certain behaviors can be very surprising. We had the issue with 64bit sin/cos which were probably precise enough but code failed because it wasn't monotonous enough (1 or 2 ULP jitters where the numerical value was ~1)...
If we decide against 4ULP for lower precision fps too, then there may still be a path to enable "fast but low precision". I am a bit wary about these low precision versions in NumPy, but I think there are also strong opinions that NumPy should not sacrifice speed too much.
Do we have a higher precision fp16 version and know the speed trade-off?
BiradarSiddhant02 commentedon Feb 19, 2026
I'm planning to put together a detailed benchmark and report comparing different fp16/bf16 math implementations. I may open a repo and link it here.
In the meantime, this is worth reading: https://www.pythontutorials.net/blog/float16-is-much-slower-than-float32-and-float64-in-numpy/
As that article shows, the speed argument for fp16 is already compromised at the instruction level on CPUs, the emulation overhead from converting to/from fp32 erases any throughput advantage. Given that, there's little remaining justification for sacrificing precision in fp16 math functions. If we're not actually getting the speed, we may as well get the accuracy.
seberg commentedon Feb 19, 2026
I doubt that is relevant. These changes here are precisely because float16 isn't just done in float32 anymore in practice which was the case before this change.
r-devulap commentedon Feb 20, 2026
It doesn’t look like it. The repository only includes the 4‑ULP variants of the float16 functions: https://github.com/numpy/SVML/tree/main/linux/avx512. I’m no longer at Intel, but I can try reaching out to a few folks to see whether the 1‑ULP versions can be open‑sourced. If it happens, it may take some time before they make it upstream.
In the meantime, should we consider reverting the current tanh implementation?
seberg commentedon Feb 20, 2026
Yeah probably. I have to see what other functions we have with low precision here.
r-devulap commentedon Feb 20, 2026
See #23351. The functions and corresponding errors are listed at the top.