num_ord/
lib.rs

1#![no_std]
2#![warn(
3    invalid_html_tags,
4    missing_debug_implementations,
5    trivial_casts,
6    unused_lifetimes,
7    unused_import_braces
8)]
9#![deny(missing_docs, unaligned_references)]
10
11//! # num-ord
12//!
13//! This crate provides a numerically ordered wrapper type, [`NumOrd`]. This
14//! type implements the [`PartialOrd`] and [`PartialEq`] traits for all the
15//! possible combinations of built-in integer types, in a mathematically correct
16//! manner without overflows.
17//!
18//! For example, comparing an `x: i64` and a `y: f64` is actually quite
19//! difficult.  Neither `(x as f64) < y` nor `x < (y as i64)` is correct. But
20//! `NumOrd(x) < NumOrd(y)` is:
21//! ```rust
22//! use num_ord::NumOrd;
23//!
24//! let x = 3_i64;
25//! let y = 3.5_f64;
26//! assert_eq!(x < (y as i64), false); // Incorrect.
27//! assert_eq!(NumOrd(x) < NumOrd(y), true); // Correct.
28//!
29//! let x = 9007199254740993_i64;
30//! let y = 9007199254740992_f64;
31//! assert_eq!(format!("{}", y), "9007199254740992"); // No rounded constant trickery!
32//! assert_eq!((x as f64) <= y, true); // Incorrect.
33//! assert_eq!(NumOrd(x) <= NumOrd(y), false); // Correct.
34//! ```
35
36use core::cmp::Ordering;
37
38
39/// A numerically ordered wrapper type.
40///
41/// See the crate docs for more details.
42#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
43#[repr(transparent)]
44pub struct NumOrd<T>(pub T);
45
46
47macro_rules! common_type_impl_body {
48    ($Lhs:ty, $Rhs:ty, $CommonT:ty, $lhs:expr, $rhs:expr, $op:ident, $less:expr, $greater:expr, $nan:expr) => {{
49        ($lhs as $CommonT).$op(&($rhs as $CommonT))
50    }};
51}
52
53macro_rules! int_float_impl_body {
54    ($IntT:ty, $FloatT:ty, $_UnusedT:ty, $lhs:expr, $rhs:expr, $op:ident, $less:expr, $greater:expr, $nan:expr) => {{
55        let lhs = $lhs;
56        let rhs = $rhs;
57
58        // Range in which FloatT is dense in the integers.
59        const FLOAT_DENSE_INT_MIN: $IntT =
60            (0 as $IntT).saturating_sub(1) << <$FloatT>::MANTISSA_DIGITS;
61        const FLOAT_DENSE_INT_MAX: $IntT = 1 << <$FloatT>::MANTISSA_DIGITS;
62
63        // One above IntT::MAX as FloatT. May be infinite.
64        const INT_MAX_POWER_OF_TWO: $IntT = <$IntT>::MAX ^ (<$IntT>::MAX >> 1);
65        const INT_ONE_ABOVE_MAX_AS_FLOAT: $FloatT = 2.0 * (INT_MAX_POWER_OF_TWO as $FloatT);
66
67        if FLOAT_DENSE_INT_MIN <= lhs && lhs <= FLOAT_DENSE_INT_MAX {
68            // lhs is exactly representable as an integer valued float.
69            (lhs as $FloatT).$op(&rhs)
70        } else if INT_ONE_ABOVE_MAX_AS_FLOAT <= rhs {
71            $less
72        } else if <$IntT>::MIN as $FloatT > rhs {
73            $greater
74        } else if rhs.is_nan() {
75            $nan
76        } else {
77            // The rounding to integer can't affect the outcome, since we know that
78            // `lhs` is sufficiently large such that if `rhs` is close, it must be
79            // an integer.
80            lhs.$op(&(rhs as $IntT))
81        }
82    }};
83}
84
85// Must have IntT <= UintT in width.
86macro_rules! int_uint_impl_body {
87    ($IntT:ty, $UintT:ty, $_UnusedT:ty, $lhs:expr, $rhs:expr, $op:ident, $less:expr, $greater:expr, $nan:expr) => {{
88        let lhs = $lhs;
89        let rhs = $rhs;
90
91        if lhs < 0 {
92            $less
93        } else {
94            (lhs as $UintT).$op(&rhs)
95        }
96    }};
97}
98
99macro_rules! apply_impl_body {
100    ($impl_body:ident, $Lhs:ty, $Rhs:ty, $CommonT:ty) => {
101        impl PartialEq<NumOrd<$Rhs>> for NumOrd<$Lhs> {
102            fn eq(&self, other: &NumOrd<$Rhs>) -> bool {
103                $impl_body!($Lhs, $Rhs, $CommonT, self.0, other.0, eq, false, false, false)
104            }
105        }
106
107        impl PartialOrd<NumOrd<$Rhs>> for NumOrd<$Lhs> {
108            fn partial_cmp(&self, other: &NumOrd<$Rhs>) -> Option<Ordering> {
109                $impl_body!(
110                    $Lhs,
111                    $Rhs,
112                    $CommonT,
113                    self.0,
114                    other.0,
115                    partial_cmp,
116                    Some(Ordering::Less),
117                    Some(Ordering::Greater),
118                    None
119                )
120            }
121
122            fn lt(&self, other: &NumOrd<$Rhs>) -> bool {
123                $impl_body!($Lhs, $Rhs, $CommonT, self.0, other.0, lt, true, false, false)
124            }
125
126            fn le(&self, other: &NumOrd<$Rhs>) -> bool {
127                $impl_body!($Lhs, $Rhs, $CommonT, self.0, other.0, le, true, false, false)
128            }
129
130            fn gt(&self, other: &NumOrd<$Rhs>) -> bool {
131                $impl_body!($Lhs, $Rhs, $CommonT, self.0, other.0, gt, false, true, false)
132            }
133
134            fn ge(&self, other: &NumOrd<$Rhs>) -> bool {
135                $impl_body!($Lhs, $Rhs, $CommonT, self.0, other.0, ge, false, true, false)
136            }
137        }
138
139        // Reverse implementation.
140        impl PartialEq<NumOrd<$Lhs>> for NumOrd<$Rhs> {
141            fn eq(&self, other: &NumOrd<$Lhs>) -> bool {
142                other == self
143            }
144        }
145
146        impl PartialOrd<NumOrd<$Lhs>> for NumOrd<$Rhs> {
147            fn partial_cmp(&self, other: &NumOrd<$Lhs>) -> Option<Ordering> {
148                other.partial_cmp(self).map(|o| o.reverse())
149            }
150
151            fn lt(&self, other: &NumOrd<$Lhs>) -> bool {
152                other > self
153            }
154
155            fn le(&self, other: &NumOrd<$Lhs>) -> bool {
156                other >= self
157            }
158
159            fn gt(&self, other: &NumOrd<$Lhs>) -> bool {
160                other < self
161            }
162
163            fn ge(&self, other: &NumOrd<$Lhs>) -> bool {
164                other <= self
165            }
166        }
167    };
168}
169
170apply_impl_body!(int_float_impl_body, i64, f32, ());
171apply_impl_body!(int_float_impl_body, i128, f32, ());
172apply_impl_body!(int_float_impl_body, i64, f64, ());
173apply_impl_body!(int_float_impl_body, i128, f64, ());
174apply_impl_body!(int_float_impl_body, u64, f32, ());
175apply_impl_body!(int_float_impl_body, u128, f32, ());
176apply_impl_body!(int_float_impl_body, u64, f64, ());
177apply_impl_body!(int_float_impl_body, u128, f64, ());
178
179apply_impl_body!(int_uint_impl_body, i8, u128, ());
180apply_impl_body!(int_uint_impl_body, i16, u128, ());
181apply_impl_body!(int_uint_impl_body, i32, u128, ());
182apply_impl_body!(int_uint_impl_body, i64, u128, ());
183apply_impl_body!(int_uint_impl_body, i128, u128, ());
184
185
186macro_rules! impl_common_type {
187    ($($T:ty, $U:ty => $C:ty;)*) => {$(
188        apply_impl_body!(common_type_impl_body, $T, $U, $C);
189    )*}
190}
191
192impl_common_type! {
193    // See tools/gen.py.
194      u8,   i8 =>  i16;
195      u8,  u16 =>  u16;
196      u8,  i16 =>  i16;
197      u8,  u32 =>  u32;
198      u8,  i32 =>  i32;
199      u8,  u64 =>  u64;
200      u8,  i64 =>  i64;
201      u8, u128 => u128;
202      u8, i128 => i128;
203      u8,  f32 =>  f32;
204      u8,  f64 =>  f64;
205      i8,  u16 =>  i32;
206      i8,  i16 =>  i16;
207      i8,  u32 =>  i64;
208      i8,  i32 =>  i32;
209      i8,  u64 => i128;
210      i8,  i64 =>  i64;
211      i8, i128 => i128;
212      i8,  f32 =>  f32;
213      i8,  f64 =>  f64;
214     u16,  i16 =>  i32;
215     u16,  u32 =>  u32;
216     u16,  i32 =>  i32;
217     u16,  u64 =>  u64;
218     u16,  i64 =>  i64;
219     u16, u128 => u128;
220     u16, i128 => i128;
221     u16,  f32 =>  f32;
222     u16,  f64 =>  f64;
223     i16,  u32 =>  i64;
224     i16,  i32 =>  i32;
225     i16,  u64 => i128;
226     i16,  i64 =>  i64;
227     i16, i128 => i128;
228     i16,  f32 =>  f32;
229     i16,  f64 =>  f64;
230     u32,  i32 =>  i64;
231     u32,  u64 =>  u64;
232     u32,  i64 =>  i64;
233     u32, u128 => u128;
234     u32, i128 => i128;
235     u32,  f32 =>  f64;
236     u32,  f64 =>  f64;
237     i32,  u64 => i128;
238     i32,  i64 =>  i64;
239     i32, i128 => i128;
240     i32,  f32 =>  f64;
241     i32,  f64 =>  f64;
242     u64,  i64 => i128;
243     u64, u128 => u128;
244     u64, i128 => i128;
245     i64, i128 => i128;
246     f32,  f64 =>  f64;
247}