-
Notifications
You must be signed in to change notification settings - Fork 13.4k
Implement total_cmp for f32, f64 #72568
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
Changes from 1 commit
68bab3e
b6eec22
d6650e0
bd68de8
6973fd7
8bc31ff
66da735
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -810,4 +810,77 @@ impl f32 { | |
pub fn from_ne_bytes(bytes: [u8; 4]) -> Self { | ||
Self::from_bits(u32::from_ne_bytes(bytes)) | ||
} | ||
|
||
/// Returns an ordering between self and other values. | ||
/// Unlike the standard partial comparison between floating point numbers, | ||
/// this comparison always produces an ordering in accordance to | ||
/// the totalOrder predicate as defined in IEEE 754 (2008 revision) | ||
/// floating point standard. The values are ordered in following order: | ||
/// - Negative quiet NaN | ||
/// - Negative signaling NaN | ||
/// - Negative infinity | ||
/// - Negative numbers | ||
/// - Negative subnormal numbers | ||
/// - Negative zero | ||
/// - Positive zero | ||
/// - Positive subnormal numbers | ||
/// - Positive numbers | ||
/// - Positive infinity | ||
/// - Positive signaling NaN | ||
/// - Positive quiet NaN | ||
/// | ||
/// # Example | ||
/// ``` | ||
/// #![feature(total_cmp)] | ||
/// struct GoodBoy { | ||
/// name: String, | ||
/// weight: f32, | ||
/// } | ||
/// | ||
/// let mut bois = vec![ | ||
/// GoodBoy { name: "Pucci".to_owned(), weight: 0.1 }, | ||
/// GoodBoy { name: "Woofer".to_owned(), weight: 99.0 }, | ||
/// GoodBoy { name: "Yapper".to_owned(), weight: 10.0 }, | ||
/// GoodBoy { name: "Chonk".to_owned(), weight: f32::INFINITY }, | ||
/// GoodBoy { name: "Abs. Unit".to_owned(), weight: f32::NAN }, | ||
/// GoodBoy { name: "Floaty".to_owned(), weight: -5.0 }, | ||
/// ]; | ||
/// | ||
/// bois.sort_by(|a, b| a.weight.total_cmp(&b.weight)); | ||
/// # assert!(bois.into_iter().map(|b| b.weight) | ||
/// # .zip([-5.0, 0.1, 10.0, 99.0, f32::INFINITY, f32::NAN].iter()) | ||
/// # .all(|(a, b)| a.to_bits() == b.to_bits())) | ||
/// ``` | ||
#[must_use = "method returns a new number and does not mutate the original value"] | ||
#[unstable(feature = "total_cmp", issue = "none")] | ||
#[inline] | ||
pub fn total_cmp(&self, other: &Self) -> crate::cmp::Ordering { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this implementation sourced from somewhere else? Might be nice to link to that for easy reference. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The code is from scratch but the idea was adapted from an earlier thread here: rust-lang/rfcs#1249 (comment) |
||
let mut left = self.to_bits() as i32; | ||
let mut right = other.to_bits() as i32; | ||
|
||
// In case of negatives, flip all the bits expect the sign | ||
golddranks marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// to achieve a similar layout as two's complement integers | ||
// | ||
// Why does this work? IEEE 754 floats consist of three fields: | ||
// Sign bit, exponent and mantissa. The set of exponent and mantissa | ||
// fields as a whole have the property that their bitwise order is | ||
// equal to the numeric magnitude where the magnitude is defined. | ||
// The magnitude is not normally defined on NaN values, but | ||
// IEEE 754 totalOrder defines the NaN values also to follow the | ||
// bitwise order. This leads to order explained in the doc comment. | ||
// However, the representation of magnitude is the same for negative | ||
// and positive numbers – only the sign bit is different. | ||
// To easily compare the floats as signed integers, we need to | ||
// flip the exponent and mantissa bits in case of negative numbers. | ||
// We effectively convert the numbers to "two's complement" form. | ||
if left < 0 { | ||
// i32::MAX corresponds the bit pattern of "all ones expect for the sign bit" | ||
golddranks marked this conversation as resolved.
Show resolved
Hide resolved
|
||
left ^= i32::MAX | ||
}; | ||
if right < 0 { | ||
right ^= i32::MAX | ||
}; | ||
|
||
left.cmp(&right) | ||
} | ||
} |
Uh oh!
There was an error while loading. Please reload this page.