tiny_ordered_float/
f64.rs

1use core::cmp::Ordering;
2use core::cmp::{Eq, Ord, PartialEq, PartialOrd};
3use core::hash::{Hash, Hasher};
4use core::option::{Option, Option::Some};
5
6pub struct OrderedFloat(pub f64);
7
8// canonical raw bit patterns (for hashing)
9const CANONICAL_NAN_BITS: u64 = 0x7ff8000000000000u64;
10
11#[inline(always)]
12fn canonicalize_signed_zero(x: f64) -> f64 {
13    // -0.0 + 0.0 == +0.0 under IEEE754 roundTiesToEven rounding mode,
14    // which Rust guarantees. Thus by adding a positive zero we
15    // canonicalize signed zero without any branches in one instruction.
16    x + 0.0
17}
18
19impl Hash for OrderedFloat {
20    fn hash<H: Hasher>(&self, state: &mut H) {
21        let bits = if self.0.is_nan() {
22            CANONICAL_NAN_BITS
23        } else {
24            canonicalize_signed_zero(self.0).to_bits()
25        };
26
27        bits.hash(state)
28    }
29}
30
31impl Eq for OrderedFloat {}
32
33impl PartialEq for OrderedFloat {
34    #[inline]
35    fn eq(&self, other: &OrderedFloat) -> bool {
36        if self.0.is_nan() {
37            other.0.is_nan()
38        } else {
39            self.0 == other.0
40        }
41    }
42}
43
44impl PartialOrd for OrderedFloat {
45    #[inline]
46    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
47        Some(self.cmp(other))
48    }
49
50    #[inline]
51    fn lt(&self, other: &Self) -> bool {
52        !self.ge(other)
53    }
54
55    #[inline]
56    fn le(&self, other: &Self) -> bool {
57        other.ge(self)
58    }
59
60    #[inline]
61    fn gt(&self, other: &Self) -> bool {
62        !other.ge(self)
63    }
64
65    #[inline]
66    fn ge(&self, other: &Self) -> bool {
67        // We consider all NaNs equal, and NaN is the largest possible
68        // value. Thus if self is NaN we always return true. Otherwise
69        // self >= other is correct. If other is also not NaN it is trivially
70        // correct, and if it is we note that nothing can be greater or
71        // equal to NaN except NaN itself, which we already handled earlier.
72        self.0.is_nan() | (self.0 >= other.0)
73    }
74}
75
76impl Ord for OrderedFloat {
77    #[inline]
78    fn cmp(&self, other: &Self) -> Ordering {
79        #[allow(clippy::comparison_chain)]
80        if self < other {
81            Ordering::Less
82        } else if self > other {
83            Ordering::Greater
84        } else {
85            Ordering::Equal
86        }
87    }
88}