Fix Ord and PartialOrd differing for FloatOrd and optimize implementation#12711
Fix Ord and PartialOrd differing for FloatOrd and optimize implementation#12711alice-i-cecile merged 3 commits intobevyengine:mainfrom
Ord and PartialOrd differing for FloatOrd and optimize implementation#12711Conversation
dd29db6 to
19223ee
Compare
| assert_eq!(one.cmp(&zero), Ordering::Greater); | ||
| assert_eq!(zero.cmp(&one), Ordering::Less); | ||
| } | ||
| } |
There was a problem hiding this comment.
could we add some tests for le too, since the behavior there is a little weird and should match cmp?
There was a problem hiding this comment.
Done for all operators, also added a test for the Hash impl.
bushrat011899
left a comment
There was a problem hiding this comment.
I think this is a good idea, but I personally don't like that the core implementation of the behaviour is split between PartialOrd::le and PartialEq::eq. If instead we provide an explicit implementation for Ord::cmp, all the other methods can be derived/implied:
// Actual Implementation
impl Ord for FloatOrd {
fn cmp(&self, other: &Self) -> Ordering {
self.0.partial_cmp(&other.0).unwrap_or_else(|| {
// Comparison could only fail if one of self or other is NaN.
// All NaN values are equivalent and less than all non-NaN values
if !other.0.is_nan() {
Ordering::Less
} else if !self.0.is_nan() {
Ordering::Greater
} else {
Ordering::Equal
}
})
}
}
// Derived from Above
impl PartialEq for FloatOrd {
fn eq(&self, other: &Self) -> bool {
self.cmp(other).is_eq()
}
}
impl PartialOrd for FloatOrd {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Eq for FloatOrd {}I personally prefer this, since there is only 1 function to actually test and reason around, and it's very clear how it relates to all other comparisons.
I agree that this would be easier to maintain and that was the very first thing I tried, but the produced assembly is worse (notably, having It seems that keeping the current |
43c1f1a to
dc01360
Compare
|
This will conflict with #12732: we should be careful not to lose this work in the process. |
Unfortunate, but I completely agree. The performance of this item is important enough to make the sacrifice reasonable. |
Objective
FloatOrdcurrently has a different comparison behavior between its derivedPartialOrdimpl and manually implementedOrdimpl (TheOrddoc says this is a logic error). This might be a problem for somestdcontainers/algorithms if they rely on both matching, and a footgun for Bevy users.Solution
PartialEqandOrdimpls ofFloatOrdwith some equivalent ones producing better assembly.PartialOrdwith the same behavior asOrd, implement the comparison operators.I first tried using a match-based implementation similar to the
PartialOrdimpl of the std (with added NaN ordering) but I couldn't get it to produce non-branching assembly. The current implementation is based on the one from theordered_floatcrate, adapted since it uses a different ordering. Should this be mentionned somewhere in the code?Changelog
Fixed
FloatOrdnow uses the same ordering for itsPartialOrdandOrdimplementations.Migration Guide
PartialOrdbehaviour ofFloatOrd, it has changed from matchingf32to matchingFloatOrd'sOrdordering, never returningNone.