-
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
sort: Float64Slice.Less does not treat -0 as less than +0 #33440
Comments
but |
It's entirely reasonable to keep the current behavior, but I should note that the current implementation treats It seems odd to me that the current implementation special cases one edge case of floating points and not another. |
But that is documented in the sort package. You have to decide how to handle NaNs one way or the other for sorting, whereas for -0 (and -Inf and +Inf) there is a natural ordering that is part of IEEE floating point and implemented by |
If we really wanted to make Do we want that? Keep in mind you can always use (Aside: Sort and Stable probably still produce implementation-defined results when the comparison function isn't transitive. That's not what is happening for floats, although it is certainly in weird territory near there.) |
Note that IEEE-754 defines a total-ordering predicate (5.10), but I do not know of any sorting library or hardware that implements it. Although it can be severely optimized, here it is for reference:
A playground link: https://play.golang.org/p/NsdlMHGI2A5 |
Thanks. I can definitely see an argument for using that ordering for |
Using something standard would be nice. The spec also says (5.10.d.3.iii) |
I think the final revision of the spec (https://ieeexplore.ieee.org/document/4610935) says in 5.10.d.3.iii: I wonder how big the breakage would be if the NaN ordering was changed. How often do people sort NaNs? |
Also it seems that IEEE defined total order this way for easy implementation, as explained here: rust-lang/rust#53938. This lets one implement this operation as a comparison of two's complement integers: https://play.golang.org/p/TsOGodZlBd-. func (p Float64Slice) Less(i, j int) bool {
x := int64(math.Float64bits(p[i]))
y := int64(math.Float64bits(p[j]))
x ^= int64(uint64(x>>63) >> 1)
y ^= int64(uint64(y>>63) >> 1)
return x < y
} |
Probably not very often. But that cuts both ways - why would it be worth a backwards-incompatible change for a feature that is seldom used? |
It's probably not worth it, considering that this behavior was introduced just before the 1.0 release( https://golang.org/cl/4805051). Although, maybe it's worth adding a We also have the option of adopting only part of the standard's total-ordering predicate, while remaining backwards-compatible. For instance, if we only wanted to specify the -0/+0 behavior, we could just adopt 5.10.c, i.e.
However, I think that the NaN ordering should not change, since Go's floating-point operations don't produce signalling NaNs in the first place, and sNaNs will only come from external sources. |
Making any change here will require introducing an import to the sort package, either math or unsafe. This is because the uint64 representation of a float64 is needed, at the minimum to retrieve the sign bit. That being said, because signed zero is not exposed at the language level, I think it is best to document that Float64Slice does not sort -0 before +0. Zeroes are grouped together, similar to how NaNs are. |
@smasher164 What do you mean by "signed zero is not exposed at the language level"? I think that's false, considering that Go follows IEEE 754 and that Go behaves like this (it is easy to make and use a signed zero value): https://play.golang.org/p/h7LnQtFSDaL Regarding the main topic of this issue (whether negative zero should be considered to be less than positive zero when sorting), I suspect the answer is yes, and futhermore that this is more important than NaN sort order. The reason is that signed zero matters when computing some mathematical functions, and as such could conceivably break someone's numeric work; while, on the other hand, a NaN only produces NaNs anyway, it's payload is usually unused, and when it is used it's probably used for debugging so the sort order wouldn't matter probably anyway. |
You're right. I forgot the case that a float variable containing 0.0 is negated. Also, regarding my previous comment about introducing an import, I think that's obviated by the recent trend of using |
Using Go1.12
Consider this snippet:
This currently prints:
I expected this to print:
Either this case be documented or we fix the
Float64Slice.Less
method.The text was updated successfully, but these errors were encountered: