-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
NAN
constants have different signs on compiler versus interpreter
#12382
Comments
Interestingly, on my Mac the output is the same for both compiled and interpreted mode. Which makes things even stranger, as the interpreted should be platform agnostic in my mind... |
This seems to come from: crystal/src/compiler/crystal/interpreter/compiler.cr Lines 351 to 354 in a5054ce
Because in crystal compiled mode (on my machine): pp! Float32::NAN.to_f32.unsafe_as(Int32) # => 2143289344 (0x7fc00000)
pp! (-Float32::NAN).to_f32.unsafe_as(Int32) # => 2143289344 (0x7fc00000) Which is very weird because # without to_f32
pp! Float32::NAN.unsafe_as(Int32) # => 2143289344 (0x7fc00000)
pp! (-Float32::NAN).unsafe_as(Int32) # => 2143289344 (0x7fc00000)
# with UInt32
pp! Float32::NAN.unsafe_as(UInt32) # => 2143289344 (0x7fc00000)
pp! (-Float32::NAN).unsafe_as(UInt32) # => 2143289344 (0x7fc00000) So there a problem how crystal cast however: pp! 0x7fc00000.unsafe_as(Float32) # => NaN
pp! 0xffc00000.unsafe_as(Float32) # => -NaN Works on both compiled and interpreter. crystal -v
|
There is no -NaN here. They're all just NaN. You can never get -NaN anywhere in Crystal unless you artificially construct it (for example via |
Okay, thanks for explanations, that make more sense on what going on. The error stay mysterious however. |
crystal/src/compiler/crystal/interpreter/instructions.cr Lines 722 to 725 in 994c70b
We could see that with: code: begin
pp! a, b
pp! (a / b)
end, The thing is that in compiled mode: 0f32 / 0f32 # => NaN
a = 0f32
a / a # => -NaN
a / 0f32 # => -NaN
0f32 / a # => -NaN and also: 0i32 / 0i32 # => -NaN The sign differs if argument come from literal or from a variable. When the code On way to solve the issue is to define However |
That's a great catch. Looking at the LLVM IR that these expressions produce, it shows that LLVM just hard codes literals: ; 0f32/0f32
movss .LCPI2209_0(%rip), %xmm0
retq
variable: ; a = 0f32; a / a
xorps %xmm0, %xmm0
movss %xmm0, -4(%rsp)
movss -4(%rsp), %xmm0
divss -4(%rsp), %xmm0
retq I'm not sure what's the best way to deal about this, but at least it seems to be clear whats happening and why. |
NAN
constants have different signs on compiler versus interpreter
The same snippet actually gives all positive NaNs in compiled release mode. Similar results happen in C: https://godbolt.org/z/d4v1hn5EG The sign bit of NaNs does matter on a few IEEE 754 operations, because they "treat floating-point numbers and NaNs alike"; those are An alternative is defining |
Not sure if hard coding a specific NaN binary representation is a good idea. Letting LLVM figure that out seems like a good move. Perhaps we should just ignore the signedness of NaN, as well as any explicit value. There's no guarantee what you'll get at runtime. And it just doesn't matter. |
IEEE 754-2019 says:
So #12242 and #12244 invoke unspecified behavior 🙄 This clause is not in IEEE 754-2008, but if we rely on |
At this rate I believe we should display all NaNs as positive, in both |
In compiled mode, not-a-numbers have a positive sign from their binary representation:
The same code shows that
Float32::NAN
is negative on the interpreter:This affects #12242 and #12244.
The text was updated successfully, but these errors were encountered: