-
Notifications
You must be signed in to change notification settings - Fork 17.7k
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
cmd/compile: constant propagation in compiler converts signaling NaN to quiet NaN #36400
Comments
Here's a reproducer, that shouldn't really fail:
It looks like this is just a constant-folding bug in the compiler.
I think this happens because we represent constants internally as float64, even when they are representing a float32 constant. So we run into #36399. Maybe we should change the internal representation for Const32f to avoid this problem. |
This has been an issue since 1.10, and there is an easy workaround, so not worth putting into 1.14. |
Change https://golang.org/cl/213477 mentions this issue: |
@randall77 Thanks for looking into this. Although the workaround works for some scenarios, it isn't working for mine. I'm writing a generic CBOR encoder/decoder in Go that can encode floats to the smallest floating-point type that preserves original value (including sNaN). Other languages like C have a generic CBOR library that can do this. In the following code:
Are there any workarounds I can use with Go 1.12+ until this is fixed in Go 1.5?
|
This compiles and runs:
Unfortunately it converts 32->64->32 under the covers (inside Convert). |
Change https://golang.org/cl/213497 mentions this issue: |
I'm not sure what workarounds you might do. For the reflect issue, you might need to delve into unsafe to get the underlying value without conversion. |
@fxamacker please keep in mind Go's unsafe package warns:
Not using unsafe is also something your library mentions repeatedly as an advantage. |
@randall77 Thanks again for spending time on this issue and suggesting workarounds.
I can't use Early adopters of my fairly new CBOR library are primarily in the field of cryptography and security, so I think avoiding |
I'll ask around and see what other people think. Don't get your hopes up. From https://github.com/golang/go/wiki/Go-Release-Cycle, about the freeze:
The fix is pretty low risk. But I don't think it qualifies as high reward. Of course, reasonable people can disagree about that... |
@randall77 Thanks so much! Please let me know. In case it helps, CTAP2 Canonical CBOR (used by FIDO2, W3C WebAuthn) specifies:
W3C requires WebAuthn to use CTAP2 Canonical CBOR:
https://www.w3.org/TR/webauthn/ Edit: added quote from WebAuthn and link to W3C WebAuthn |
I asked around, and I don't think anyone is interested in adopting the reflect change for 1.14. It's just too late in the cycle. The release candidate will hopefully go out this week. I did think of a possible workaround - if you can get an addressable
|
Never mind, addressability is not required, as we can make an addressable copy. You can do this:
|
@randall77 your workaround works great! 👍 I really appreciate your time and I don't know what to say except that you set the bar really high for open source projects. |
When converting from float32->float64->float32, any signal NaNs get converted to quiet NaNs. Avoid that so using reflect.Value.Convert between two float32 types keeps the signal bit of NaNs. Update #36400 Change-Id: Ic4dd04c4be7189d2171d12b7e4e8f7cf2fb22bb4 Reviewed-on: https://go-review.googlesource.com/c/go/+/213497 Run-TryBot: Keith Randall <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Ian Lance Taylor <[email protected]>
CL reverted, reopening. |
The reflect fix doesn't work on the 387 port. The 387 port can't even load a float into a register and store it right back to memory without squashing the signaling bit. Others have run into this: samtools/hts-specs#145 I'm inclined to just punt on 387. I think all the other ports are ok in this regard. |
Change https://golang.org/cl/221790 mentions this issue: |
Change https://golang.org/cl/221792 mentions this issue: |
Trying this CL again, with a fixed test that allows platforms to disagree on the exact behavior of converting NaNs. We store 32-bit floating point constants in a 64-bit field, by converting that 32-bit float to 64-bit float to store it, and convert it back to use it. That works for *almost* all floating-point constants. The exception is signaling NaNs. The round trip described above means we can't represent a 32-bit signaling NaN, because conversions strip the signaling bit. To fix this issue, just forbid NaNs as floating-point constants in SSA form. This shouldn't affect any real-world code, as people seldom constant-propagate NaNs (except in test code). Additionally, NaNs are somewhat underspecified (which of the many NaNs do you get when dividing 0/0?), so when cross-compiling there's a danger of using the compiler machine's NaN regime for some math, and the target machine's NaN regime for other math. Better to use the target machine's NaN regime always. Update #36400 Change-Id: Idf203b688a15abceabbd66ba290d4e9f63619ecb Reviewed-on: https://go-review.googlesource.com/c/go/+/221790 Run-TryBot: Keith Randall <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Josh Bleecher Snyder <[email protected]>
Change https://golang.org/cl/227860 mentions this issue: |
Missed as part of CL 221790. It isn't just * and / that can make NaNs. Update #36400 Fixes #38359 Change-Id: I3fa562f772fe03b510793a6dc0cf6189c0c3e652 Reviewed-on: https://go-review.googlesource.com/c/go/+/227860 Run-TryBot: Keith Randall <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Alberto Donizetti <[email protected]> Reviewed-by: Cuong Manh Le <[email protected]>
What version of Go are you using (
go version
)?Does this issue reproduce with the latest release?
Yes, same result with both go1.12.12 and go 1.13.5.
What operating system and processor architecture are you using (
go env
)?go env
OutputWhat did you do?
reflect.Value
object from float32 sNaN.Value.Float()
on float32 sNaN, but it unexpectedly returns float64 qNaN.This is different behavior than casting float32 sNaN to a float64, which correctly returns float64 sNaN.
This bug is blocking fxamacker/cbor#91. See fxamacker/cbor#93 for more info. This bug may be related to #36399.
https://play.golang.org/p/T7orv6p_C6h
What did you expect to see?
Value.Float()
of float32 sNaN should return float64 sNaN.This is expected because casting float32 sNaN to float64 returns sNaN.
What did you see instead?
Value.Float()
of float32 sNaN returns float64 qNaN instead.This is different result from casting float32 sNaN to float64.
The text was updated successfully, but these errors were encountered: