News for hackers. Stuff that matters.
Re:Operator constraints in Go by jeffbee on Monday May 23, 2022 @03:14PM (#31483683)
Hrmm. How do float32 and float64 satisfy constraints.Ordered? The Go spec (which is almost uselessly vague and will some day contain 1000 pages of caveats) just says that floats are "comparable and ordered, as defined by the IEEE-754 standard" but of course that standard doesn't really say that, it specifically says that NaN are unordered. I just checked quickly with Compiler Explorer and float32<float32 emits UCOMISS on x86, which makes sense, resulting in 42.0<NaN and NaN<42.0 both being false, as expected. So how does it handle the constraint for float?
Re:Operator constraints in Go by jerf on Monday May 23, 2022 @03:30PM (#31483846)
Basically, it doesn't work for sorting. https://pkg.go.dev/golang.org/x/exp/slices#Sort mentions this explicitly and has a suggested alternative solution. You can see this in action here: https://go.dev/play/p/6GuDa8ziU0L

Note in the second case, that's not "Go" sorting NaNs to the front, that's the requested result from the passed-in function. You could create any result you wanted with a suitable choice of such function.

This isn't particularly special to Go. Most languages have "quirks", to put it charitably, around floats. Even Haskell would have similar behavior. It's really the IEEE standard that is broken, arguably; defining an entity that is not equal to itself proceeds to effectively break a huge swathe of things that you may not have even realized was based on the presumption that the identity property holds, because... why would you think about what is based on that? It's so foundational. As anyone who has dealt with NaNs for any period of time can attest, it's really difficult to work with such entities.

(Similarly, while I understand what they were trying to get at, I think SQL NULL should be equal to itself. I could be down with NULL < 1 being false and NULL > 1 being false but I'm not a huge fan of how it's NULL either. We've had many languages written since then with more conventional ideas about "invalid" values and how they participate in operators and I think the modern way almost everything else works is better, even if it arguably less "correct".)

Re:Operator constraints in Go by dinosaurdynasty on Monday May 23, 2022 @04:23PM (#31484367)
Rust handles this in a "correct", if annoying way: the stdlib sort requires its elements to have a "totally ordered" trait, and floating point doesn't have that trait. You have to either give a custom comparison function, or use a "non-NaN and totally orderable floating point" type some non-stdlib crates have.
Re:Operator constraints in Go by majewsky on Tuesday May 24, 2022 @07:39AM (#31490733)
This perfectly illustrates the cultural difference between Rust and Go: Rust wants to be "correct", Go wants to be "good enough".
Re:Operator constraints in Go by nerdponx on Monday May 23, 2022 @04:15PM (#31484288)
This is problematic even in dependently-typed languages like Idris, because while you can require a proof that something is not (or does not contain) NaN, it's pretty easy to call a function that is allowed to return NaN, which means that you actually cannot satisfy that proof after calling one of those functions unless you actually check again.
Re:Operator constraints in Go by jeffbee on Monday May 23, 2022 @03:41PM (#31483967)
The entire C++ STL is a huge footgun for this very reason. None of the algorithms work on floats unless you guarantee the absence of NaN (which the STL assumes without being terribly vocal about it).
Re:Operator constraints in Go by Merovius on Monday May 23, 2022 @03:33PM (#31483875)
> How do float32 and float64 satisfy constraints.Ordered?

Well, the simple answer is that constraints.Ordered is a type set which lists them: https://pkg.go.dev/golang.org/x/exp/constraints#Ordered

The other answer is of course, that float32 and float64 have a < operator, so every type listed in constraints.Ordered does, so you can use the < operator on a type parameter constrained by constraints.Orderded.

The real question you seem to be asking though, is "how does Go support < on float32 and float64 if the IEEE-754 standard says that NaN are not comparable". I can't comprehensively answer that, because I don't know that standard well enough. But for == the answer is that == is supported on floats and x == y is always false if either is NaN. Empirically, the same seems to be true for NaN <= NaN: https://go.dev/play/p/hB9CnrzpAVq

In any case, Wikipedia claims that IEEE-754 defines in fact an ordering, which is the one Go is going to use use: https://en.wikipedia.org/wiki/IEEE_754#Total-ordering_predic...