all: ordering numeric values, including NaNs, and the consequences thereof #59531
There are thorny issues around ordering of numerical values, and rather than add to existing conversations I thought it worth pulling them into their own conversation.
The questions here touch on
Go provides less than and less-than-or-equal operators that apply to strings, integers, and scalar numbers. (There is no ordering for complex values.) The proposal to add an
Except they don't, not quite. The problem is IEEE-754 NaNs. It is a peculiarity of IEEE-754 and its implementation in both hardware and most languages that NaNs do not behave like other numbers under comparison. All "less than" and related comparison operations, including "equals", return
This behavior is the key reason why the map
Similarly, sorting and such will misbehave in the presence of NaNs. This is why
Mischievously, this total ordering of NaNs is also defined by IEEE-754 but is not the standard behavior of less than, either as a hardware instruction or in a programming language.
Thus NaNs are ordered but not by the comparison operator. The consequences of this must be clear before defining any ordering constraint.
That is, the addition of an
Neither of these paths is satisfactory. So what should we do? Short answer, I don't know, but we certainly should not change the language or libraries until we have thoroughly thought this through.
I do not, at least at the moment, propose a solution. Instead I am isolating the issue in a discussion to make sure we do the right thing with
The text was updated successfully, but these errors were encountered:
No answers, just some notes.
Java implements a class
In Rust the trait
C++ and Python programmers are best advised to not sort vectors containing NaN values.
In Go we have
A conservative approach is to make the
However, I have a feeling that the effect would be that people who want to sort a slice of floating-point numbers would reach for
So there is another approach we can take. Don't permit floating-point types for
With this approach, the naïve user will use
In my experience with Rust, the situation I've seen tends to be more like, "the naïve user will use
@zephyrtronium I'm not sure I entirely understand your comment about narrowing the constraint. To be clear, you could still use the
@dr2chase A good error message would help, of course, but my guess is that most people who want to sort floating-point numbers simply don't care about NaN values at all. So it's weird to tell them that the obvious function doesn't work. Especially since we can choose to make it work.
I always assumed that the use-cases for
Sort(slice) SortFunc(slice, math.Compare)
respectively. It provides an opportunity to opt into a different comparison predicate, while preserving existing behavior. The
That being said, I agree that
I forgot to mention the delights of signed zeros. Not as problematic as NaNs, but again inconsistent with regard to traditional "less than" vs. total ordering: https://gotipplay.golang.org/p/qd16ohSUz_p
One little idea, could be to consider NaNs and signed 0 as subtypes.
But that'd be a language change and would be more involved.
Essentially, it'd allow types to be further constrainable so that a non-NaN float64 type can be defined and enforced in user code.
The mechanism would still be quite similar to what is done today for interface (assertions, assignment).
I imagine that it could solve comparison and ordering in one go. (pun half intended)
But that's quite involved I think and not sure if that complexity is warranted (maybe it would be fine, needs further investigation about use-cases).
An update on the current status.
We added the
I don't know whether there is anything else to do here. I think we're an OK place right now, but I'm definitely too close to the problem.