|
| 1 | +use core::cmp::Ordering; |
1 | 2 | use core::num::FpCategory;
|
2 | 3 | use core::ops::{Add, Div, Neg};
|
3 | 4 |
|
@@ -2210,6 +2211,89 @@ float_const_impl! {
|
2210 | 2211 | SQRT_2,
|
2211 | 2212 | }
|
2212 | 2213 |
|
| 2214 | +/// Trait for floating point numbers that provide an implementation |
| 2215 | +/// of the `totalOrder` predicate as defined in the IEEE 754 (2008 revision) |
| 2216 | +/// floating point standard. |
| 2217 | +pub trait TotalOrder { |
| 2218 | + /// Return the ordering between `self` and `other`. |
| 2219 | + /// |
| 2220 | + /// Unlike the standard partial comparison between floating point numbers, |
| 2221 | + /// this comparison always produces an ordering in accordance to |
| 2222 | + /// the `totalOrder` predicate as defined in the IEEE 754 (2008 revision) |
| 2223 | + /// floating point standard. The values are ordered in the following sequence: |
| 2224 | + /// |
| 2225 | + /// - negative quiet NaN |
| 2226 | + /// - negative signaling NaN |
| 2227 | + /// - negative infinity |
| 2228 | + /// - negative numbers |
| 2229 | + /// - negative subnormal numbers |
| 2230 | + /// - negative zero |
| 2231 | + /// - positive zero |
| 2232 | + /// - positive subnormal numbers |
| 2233 | + /// - positive numbers |
| 2234 | + /// - positive infinity |
| 2235 | + /// - positive signaling NaN |
| 2236 | + /// - positive quiet NaN. |
| 2237 | + /// |
| 2238 | + /// The ordering established by this function does not always agree with the |
| 2239 | + /// [`PartialOrd`] and [`PartialEq`] implementations. For example, |
| 2240 | + /// they consider negative and positive zero equal, while `total_cmp` |
| 2241 | + /// doesn't. |
| 2242 | + /// |
| 2243 | + /// The interpretation of the signaling NaN bit follows the definition in |
| 2244 | + /// the IEEE 754 standard, which may not match the interpretation by some of |
| 2245 | + /// the older, non-conformant (e.g. MIPS) hardware implementations. |
| 2246 | + /// |
| 2247 | + /// # Examples |
| 2248 | + /// ``` |
| 2249 | + /// use num_traits::float::TotalOrder; |
| 2250 | + /// use std::cmp::Ordering; |
| 2251 | + /// use std::{f32, f64}; |
| 2252 | + /// |
| 2253 | + /// fn check_eq<T: TotalOrder>(x: T, y: T) { |
| 2254 | + /// assert_eq!(x.total_cmp(&y), Ordering::Equal); |
| 2255 | + /// } |
| 2256 | + /// |
| 2257 | + /// check_eq(f64::NAN, f64::NAN); |
| 2258 | + /// check_eq(f32::NAN, f32::NAN); |
| 2259 | + /// |
| 2260 | + /// fn check_lt<T: TotalOrder>(x: T, y: T) { |
| 2261 | + /// assert_eq!(x.total_cmp(&y), Ordering::Less); |
| 2262 | + /// } |
| 2263 | + /// |
| 2264 | + /// check_lt(-f64::NAN, f64::NAN); |
| 2265 | + /// check_lt(f64::INFINITY, f64::NAN); |
| 2266 | + /// check_lt(-0.0_f64, 0.0_f64); |
| 2267 | + /// ``` |
| 2268 | + fn total_cmp(&self, other: &Self) -> Ordering; |
| 2269 | +} |
| 2270 | +macro_rules! totalorder_impl { |
| 2271 | + ($T:ident, $I:ident, $U:ident, $bits:expr) => { |
| 2272 | + impl TotalOrder for $T { |
| 2273 | + #[inline] |
| 2274 | + #[cfg(has_total_cmp)] |
| 2275 | + fn total_cmp(&self, other: &Self) -> Ordering { |
| 2276 | + // Forward to the core implementation |
| 2277 | + Self::total_cmp(&self, other) |
| 2278 | + } |
| 2279 | + #[inline] |
| 2280 | + #[cfg(not(has_total_cmp))] |
| 2281 | + fn total_cmp(&self, other: &Self) -> Ordering { |
| 2282 | + // Backport the core implementation (since 1.62) |
| 2283 | + let mut left = self.to_bits() as $I; |
| 2284 | + let mut right = other.to_bits() as $I; |
| 2285 | + |
| 2286 | + left ^= (((left >> ($bits - 1)) as $U) >> 1) as $I; |
| 2287 | + right ^= (((right >> ($bits - 1)) as $U) >> 1) as $I; |
| 2288 | + |
| 2289 | + left.cmp(&right) |
| 2290 | + } |
| 2291 | + } |
| 2292 | + }; |
| 2293 | +} |
| 2294 | +totalorder_impl!(f64, i64, u64, 64); |
| 2295 | +totalorder_impl!(f32, i32, u32, 32); |
| 2296 | + |
2213 | 2297 | #[cfg(test)]
|
2214 | 2298 | mod tests {
|
2215 | 2299 | use core::f64::consts;
|
@@ -2341,4 +2425,58 @@ mod tests {
|
2341 | 2425 | test_subnormal::<f64>();
|
2342 | 2426 | test_subnormal::<f32>();
|
2343 | 2427 | }
|
| 2428 | + |
| 2429 | + #[test] |
| 2430 | + fn total_cmp() { |
| 2431 | + use crate::float::TotalOrder; |
| 2432 | + use core::cmp::Ordering; |
| 2433 | + use core::{f32, f64}; |
| 2434 | + |
| 2435 | + fn check_eq<T: TotalOrder>(x: T, y: T) { |
| 2436 | + assert_eq!(x.total_cmp(&y), Ordering::Equal); |
| 2437 | + } |
| 2438 | + fn check_lt<T: TotalOrder>(x: T, y: T) { |
| 2439 | + assert_eq!(x.total_cmp(&y), Ordering::Less); |
| 2440 | + } |
| 2441 | + fn check_gt<T: TotalOrder>(x: T, y: T) { |
| 2442 | + assert_eq!(x.total_cmp(&y), Ordering::Greater); |
| 2443 | + } |
| 2444 | + |
| 2445 | + check_eq(f64::NAN, f64::NAN); |
| 2446 | + check_eq(f32::NAN, f32::NAN); |
| 2447 | + |
| 2448 | + check_lt(-0.0_f64, 0.0_f64); |
| 2449 | + check_lt(-0.0_f32, 0.0_f32); |
| 2450 | + |
| 2451 | + // x87 registers don't preserve the exact value of signaling NaN: |
| 2452 | + // https://github.com/rust-lang/rust/issues/115567 |
| 2453 | + #[cfg(not(target_arch = "x86"))] |
| 2454 | + { |
| 2455 | + let s_nan = f64::from_bits(0x7ff4000000000000); |
| 2456 | + let q_nan = f64::from_bits(0x7ff8000000000000); |
| 2457 | + check_lt(s_nan, q_nan); |
| 2458 | + |
| 2459 | + let neg_s_nan = f64::from_bits(0xfff4000000000000); |
| 2460 | + let neg_q_nan = f64::from_bits(0xfff8000000000000); |
| 2461 | + check_lt(neg_q_nan, neg_s_nan); |
| 2462 | + |
| 2463 | + let s_nan = f32::from_bits(0x7fa00000); |
| 2464 | + let q_nan = f32::from_bits(0x7fc00000); |
| 2465 | + check_lt(s_nan, q_nan); |
| 2466 | + |
| 2467 | + let neg_s_nan = f32::from_bits(0xffa00000); |
| 2468 | + let neg_q_nan = f32::from_bits(0xffc00000); |
| 2469 | + check_lt(neg_q_nan, neg_s_nan); |
| 2470 | + } |
| 2471 | + |
| 2472 | + check_lt(-f64::NAN, f64::NEG_INFINITY); |
| 2473 | + check_gt(1.0_f64, -f64::NAN); |
| 2474 | + check_lt(f64::INFINITY, f64::NAN); |
| 2475 | + check_gt(f64::NAN, 1.0_f64); |
| 2476 | + |
| 2477 | + check_lt(-f32::NAN, f32::NEG_INFINITY); |
| 2478 | + check_gt(1.0_f32, -f32::NAN); |
| 2479 | + check_lt(f32::INFINITY, f32::NAN); |
| 2480 | + check_gt(f32::NAN, 1.0_f32); |
| 2481 | + } |
2344 | 2482 | }
|
0 commit comments