-
Notifications
You must be signed in to change notification settings - Fork 12.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
LLVM is allowed to... be creative... with NANs according to Rust float semantics #134417
Comments
I believe this is expected behavior. The documentation for f32 says:
|
They are not equal, both |
@Urgau i'm curious why parsing vs using a float literal would affects signedness of |
Related:
Output:
sign seems to be same for the parsed and literal variable, but the division still executed differently. |
Even if we consider that |
The initial example can be further reduced to fn main() {
let z: f64 = std::hint::black_box(0.0);
dbg!((z / z).to_bits());
let z: f64 = 0.0;
dbg!((z / z).to_bits());
} Which on my machine prints this:
LLVM is doing some kind of optimization here that we do not have a mechanism to turn off, which is itself disturbing. Clang does the same transformation, which breaks (depending on your definition) some parts of musl's libm which try to do the equivalent of Quite vexing. |
The ability to use
...you did the same operations, and the operations had inconsistent results, so the output was similarly inconsistent? |
Could one usability improvement be that different NaNs convert to string representation looks different? now they all say "NaN". |
Is there any precedent for doing that in other language? At a glance, in Python:
But in any case, I think this whole issue went off in the wrong direction. The root cause here is that jaq's comparison seems to have been written without the knowledge that in Rust, NaN generation is nondeterministic. This nondet has technically been around for a long time due to this LLVM codegen oddity I pointed out, but it's really quite hard to run into cases where the nondeterminism becomes visible, and only rather recently have we documented that NaN generation is nondeterministic. |
@saethlin no precedent that i know, and now when i think about it maybe different strings could be confusing in some other contexts. and yeap i guess jaq will have to do this in some explicit way using |
Regarding the title, I would rather say it is x86 that is a bit creative, by setting the sign bit of the NaN if none of the inputs to the operation are themselves already NaN. This seems to be documented, for example searching for "QNaN floating-point indefinite" in Intel 64 and IA-32 Architectures Software Developer’s Manual Volume 1: Basic Architecture (Found via stackoverflow). LLVM is of course allowed to use a different NaN representation when constant folding during optimizations, and users should not rely on a specific bit representation. |
I think that for me, all is said in the following paragraph:
This is, to quote @saethlin, greatly disturbing --- but at least it's documented. I suppose that we must do with this. I'm closing this issue --- may it serve as guidance for those who trod down the same path of misery as we did. |
We have recently observed some very strange behaviour related to float handling, that seems to be linked to float parsing (01mf02/jaq#243).
Consider the following program:
This yields:
Given that for both
t
andz
, their bits are the same, I expectedtotal_cmp
to yield the same outputs; however, one yieldsGreater
, the other yieldsLess
!I then refactored the program to an (IMO) equivalent one:
Now, the output of
total_cmp
is the same!This occurs both with
cargo run
andcargo run --release
.Could this be a compiler bug?
Meta
rustc --version --verbose
:The text was updated successfully, but these errors were encountered: