diff --git a/crates/bevy_math/src/float_ord.rs b/crates/bevy_math/src/float_ord.rs index e69c6b35e0702..81af1a0365d26 100644 --- a/crates/bevy_math/src/float_ord.rs +++ b/crates/bevy_math/src/float_ord.rs @@ -10,12 +10,8 @@ use bevy_reflect::Reflect; /// A wrapper for floats that implements [`Ord`], [`Eq`], and [`Hash`] traits. /// /// This is a work around for the fact that the IEEE 754-2008 standard, -/// implemented by Rust's [`f32`] type, -/// doesn't define an ordering for [`NaN`](f32::NAN), -/// and `NaN` is not considered equal to any other `NaN`. -/// -/// Wrapping a float with `FloatOrd` breaks conformance with the standard -/// by sorting `NaN` as less than all other numbers and equal to any other `NaN`. +/// implemented by Rust's [`f32`] type, doesn't define an ordering for +/// [`NaN`](f32::NAN). #[derive(Debug, Copy, Clone)] #[cfg_attr( feature = "bevy_reflect", @@ -28,47 +24,17 @@ impl PartialOrd for FloatOrd { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } - - fn lt(&self, other: &Self) -> bool { - !other.le(self) - } - // If `self` is NaN, it is equal to another NaN and less than all other floats, so return true. - // If `self` isn't NaN and `other` is, the float comparison returns false, which match the `FloatOrd` ordering. - // Otherwise, a standard float comparison happens. - fn le(&self, other: &Self) -> bool { - self.0.is_nan() || self.0 <= other.0 - } - fn gt(&self, other: &Self) -> bool { - !self.le(other) - } - fn ge(&self, other: &Self) -> bool { - other.le(self) - } } impl Ord for FloatOrd { - #[expect( - clippy::comparison_chain, - reason = "This can't be rewritten with `match` and `cmp`, as this is `cmp` itself." - )] fn cmp(&self, other: &Self) -> Ordering { - if self > other { - Ordering::Greater - } else if self < other { - Ordering::Less - } else { - Ordering::Equal - } + self.0.total_cmp(&other.0) } } impl PartialEq for FloatOrd { fn eq(&self, other: &Self) -> bool { - if self.0.is_nan() { - other.0.is_nan() - } else { - self.0 == other.0 - } + matches!(self.cmp(other), Ordering::Equal) } } @@ -76,15 +42,7 @@ impl Eq for FloatOrd {} impl Hash for FloatOrd { fn hash(&self, state: &mut H) { - if self.0.is_nan() { - // Ensure all NaN representations hash to the same value - state.write(&f32::to_ne_bytes(f32::NAN)); - } else if self.0 == 0.0 { - // Ensure both zeroes hash to the same value - state.write(&f32::to_ne_bytes(0.0f32)); - } else { - state.write(&f32::to_ne_bytes(self.0)); - } + self.0.to_bits().hash(state); } } @@ -118,8 +76,8 @@ mod tests { fn float_ord_cmp() { assert_eq!(NAN.cmp(&NAN), Ordering::Equal); - assert_eq!(NAN.cmp(&ZERO), Ordering::Less); - assert_eq!(ZERO.cmp(&NAN), Ordering::Greater); + assert_ne!(NAN.cmp(&ZERO), Ordering::Equal); + assert_ne!(ZERO.cmp(&NAN), Ordering::Equal); assert_eq!(ZERO.cmp(&ZERO), Ordering::Equal); assert_eq!(ONE.cmp(&ZERO), Ordering::Greater); @@ -133,51 +91,24 @@ mod tests { )] fn float_ord_cmp_operators() { assert!(!(NAN < NAN)); - assert!(NAN < ZERO); - assert!(!(ZERO < NAN)); + assert_ne!(NAN, ZERO); assert!(!(ZERO < ZERO)); assert!(ZERO < ONE); assert!(!(ONE < ZERO)); assert!(!(NAN > NAN)); - assert!(!(NAN > ZERO)); - assert!(ZERO > NAN); assert!(!(ZERO > ZERO)); assert!(!(ZERO > ONE)); assert!(ONE > ZERO); assert!(NAN <= NAN); - assert!(NAN <= ZERO); - assert!(!(ZERO <= NAN)); assert!(ZERO <= ZERO); assert!(ZERO <= ONE); assert!(!(ONE <= ZERO)); assert!(NAN >= NAN); - assert!(!(NAN >= ZERO)); - assert!(ZERO >= NAN); assert!(ZERO >= ZERO); assert!(!(ZERO >= ONE)); assert!(ONE >= ZERO); } - - #[cfg(feature = "std")] - #[test] - fn float_ord_hash() { - let hash = |num| { - let mut h = std::hash::DefaultHasher::new(); - FloatOrd(num).hash(&mut h); - h.finish() - }; - - assert_ne!((-0.0f32).to_bits(), 0.0f32.to_bits()); - assert_eq!(hash(-0.0), hash(0.0)); - - let nan_1 = f32::from_bits(0b0111_1111_1000_0000_0000_0000_0000_0001); - assert!(nan_1.is_nan()); - let nan_2 = f32::from_bits(0b0111_1111_1000_0000_0000_0000_0000_0010); - assert!(nan_2.is_nan()); - assert_ne!(nan_1.to_bits(), nan_2.to_bits()); - assert_eq!(hash(nan_1), hash(nan_2)); - } }