Skip to main content

rug/float/
cmp.rs

1// Copyright © 2016–2026 Trevor Spiteri
2
3// This program is free software: you can redistribute it and/or modify it under
4// the terms of the GNU Lesser General Public License as published by the Free
5// Software Foundation, either version 3 of the License, or (at your option) any
6// later version.
7//
8// This program is distributed in the hope that it will be useful, but WITHOUT
9// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
10// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
11// details.
12//
13// You should have received a copy of the GNU Lesser General Public License and
14// a copy of the GNU General Public License along with this program. If not, see
15// <https://www.gnu.org/licenses/>.
16
17use crate::Float;
18#[cfg(feature = "integer")]
19use crate::Integer;
20#[cfg(feature = "rational")]
21use crate::Rational;
22use crate::ext::xmpfr;
23#[allow(deprecated)]
24use crate::float::SmallFloat;
25use crate::float::big::{IExpIncomplete, UExpIncomplete};
26use crate::float::{MiniFloat, Special};
27use az::Cast;
28use core::cmp::Ordering;
29
30impl PartialEq for Float {
31    #[inline]
32    fn eq(&self, other: &Float) -> bool {
33        xmpfr::equal_p(self, other)
34    }
35}
36
37impl PartialOrd for Float {
38    #[inline]
39    fn partial_cmp(&self, other: &Float) -> Option<Ordering> {
40        if xmpfr::unordered_p(self, other) {
41            None
42        } else {
43            Some(xmpfr::cmp(self, other))
44        }
45    }
46
47    #[inline]
48    fn lt(&self, other: &Float) -> bool {
49        xmpfr::less_p(self, other)
50    }
51
52    #[inline]
53    fn le(&self, other: &Float) -> bool {
54        xmpfr::lessequal_p(self, other)
55    }
56
57    #[inline]
58    fn gt(&self, other: &Float) -> bool {
59        xmpfr::greater_p(self, other)
60    }
61
62    #[inline]
63    fn ge(&self, other: &Float) -> bool {
64        xmpfr::greaterequal_p(self, other)
65    }
66}
67
68impl PartialOrd<MiniFloat> for Float {
69    #[inline]
70    fn partial_cmp(&self, other: &MiniFloat) -> Option<Ordering> {
71        self.partial_cmp(&*other.borrow())
72    }
73}
74
75impl PartialOrd<Float> for MiniFloat {
76    #[inline]
77    fn partial_cmp(&self, other: &Float) -> Option<Ordering> {
78        (*self.borrow()).partial_cmp(other)
79    }
80}
81
82impl PartialEq<MiniFloat> for Float {
83    #[inline]
84    fn eq(&self, other: &MiniFloat) -> bool {
85        self.eq(&*other.borrow())
86    }
87}
88
89impl PartialEq<Float> for MiniFloat {
90    #[inline]
91    fn eq(&self, other: &Float) -> bool {
92        (*self.borrow()).eq(other)
93    }
94}
95
96#[allow(deprecated)]
97impl PartialOrd<SmallFloat> for Float {
98    #[inline]
99    fn partial_cmp(&self, other: &SmallFloat) -> Option<Ordering> {
100        self.partial_cmp(&**other)
101    }
102}
103
104#[allow(deprecated)]
105impl PartialOrd<Float> for SmallFloat {
106    #[inline]
107    fn partial_cmp(&self, other: &Float) -> Option<Ordering> {
108        (**self).partial_cmp(other)
109    }
110}
111
112#[allow(deprecated)]
113impl PartialEq<SmallFloat> for Float {
114    #[inline]
115    fn eq(&self, other: &SmallFloat) -> bool {
116        self.eq(&**other)
117    }
118}
119
120#[allow(deprecated)]
121impl PartialEq<Float> for SmallFloat {
122    #[inline]
123    fn eq(&self, other: &Float) -> bool {
124        (**self).eq(other)
125    }
126}
127
128macro_rules! cmp {
129    ($T:ty) => {
130        impl PartialEq<$T> for Float {
131            #[inline]
132            fn eq(&self, other: &$T) -> bool {
133                self.partial_cmp(other) == Some(Ordering::Equal)
134            }
135        }
136
137        impl PartialEq<Float> for $T {
138            #[inline]
139            fn eq(&self, other: &Float) -> bool {
140                other.partial_cmp(self) == Some(Ordering::Equal)
141            }
142        }
143
144        impl PartialOrd<Float> for $T {
145            #[inline]
146            fn partial_cmp(&self, other: &Float) -> Option<Ordering> {
147                other.partial_cmp(self).map(Ordering::reverse)
148            }
149        }
150    };
151}
152
153macro_rules! cmp_i {
154    ($T:ty, |$f:ident, $o:ident| $eval:expr) => {
155        cmp! { $T }
156
157        impl PartialOrd<$T> for Float {
158            #[inline]
159            fn partial_cmp(&self, $o: &$T) -> Option<Ordering> {
160                if self.is_nan() {
161                    None
162                } else {
163                    let $f = self;
164                    Some($eval)
165                }
166            }
167        }
168    };
169}
170
171macro_rules! cmp_f {
172    ($T:ty, |$f:ident, $o:ident| $eval:expr) => {
173        cmp! { $T }
174
175        impl PartialOrd<$T> for Float {
176            #[inline]
177            fn partial_cmp(&self, $o: &$T) -> Option<Ordering> {
178                if self.is_nan() || $o.is_nan() {
179                    None
180                } else {
181                    let $f = self;
182                    Some($eval)
183                }
184            }
185        }
186    };
187}
188
189#[cfg(feature = "integer")]
190cmp_i! { Integer, |f, z| xmpfr::cmp_z(f, z) }
191#[cfg(feature = "rational")]
192cmp_i! { Rational, |f, q| xmpfr::cmp_q(f, q) }
193
194cmp_i! { i8, |f, t| xmpfr::cmp_si(f, (*t).into()) }
195cmp_i! { i16, |f, t| xmpfr::cmp_si(f, (*t).into()) }
196cmp_i! { i32, |f, t| xmpfr::cmp_si(f, (*t).into()) }
197cmp_i! { i64, |f, t| xmpfr::cmp_i64(f, *t) }
198cmp_i! { i128, |f, t| xmpfr::cmp_i128(f, *t) }
199#[cfg(target_pointer_width = "32")]
200cmp_i! { isize, |f, t| xmpfr::cmp_si(f, (*t).cast()) }
201#[cfg(target_pointer_width = "64")]
202cmp_i! { isize, |f, t| xmpfr::cmp_i64(f, (*t).cast()) }
203
204cmp_i! { u8, |f, t| xmpfr::cmp_ui(f, (*t).into()) }
205cmp_i! { u16, |f, t| xmpfr::cmp_ui(f, (*t).into()) }
206cmp_i! { u32, |f, t| xmpfr::cmp_ui(f, (*t).into()) }
207cmp_i! { u64, |f, t| xmpfr::cmp_u64(f, *t) }
208cmp_i! { u128, |f, t| xmpfr::cmp_u128(f, *t) }
209#[cfg(target_pointer_width = "32")]
210cmp_i! { usize, |f, t| xmpfr::cmp_ui(f, (*t).cast()) }
211#[cfg(target_pointer_width = "64")]
212cmp_i! { usize, |f, t| xmpfr::cmp_u64(f, (*t).cast()) }
213
214#[cfg(feature = "nightly-float")]
215cmp_f! { f16, |f, t| xmpfr::cmp_f64(f, (*t).into()) }
216cmp_f! { f32, |f, t| xmpfr::cmp_f64(f, (*t).into()) }
217cmp_f! { f64, |f, t| xmpfr::cmp_f64(f, *t) }
218
219#[cfg(feature = "nightly-float")]
220cmp! { f128 }
221
222#[cfg(feature = "nightly-float")]
223impl PartialOrd<f128> for Float {
224    #[inline]
225    fn partial_cmp(&self, o: &f128) -> Option<Ordering> {
226        let mut small = MiniFloat::from(*o);
227        PartialOrd::partial_cmp(self, small.borrow_excl())
228    }
229}
230
231cmp! { Special }
232
233impl PartialOrd<Special> for Float {
234    #[inline]
235    fn partial_cmp(&self, other: &Special) -> Option<Ordering> {
236        if self.is_nan() {
237            return None;
238        }
239        match *other {
240            Special::Zero | Special::NegZero => self.cmp0(),
241            Special::Infinity => {
242                if self.is_sign_positive() && self.is_infinite() {
243                    Some(Ordering::Equal)
244                } else {
245                    Some(Ordering::Less)
246                }
247            }
248            Special::NegInfinity => {
249                if self.is_sign_negative() && self.is_infinite() {
250                    Some(Ordering::Equal)
251                } else {
252                    Some(Ordering::Greater)
253                }
254            }
255            Special::Nan => None,
256        }
257    }
258}
259
260cmp! { UExpIncomplete }
261cmp! { IExpIncomplete }
262
263#[cfg(test)]
264mod tests {
265    #[cfg(feature = "integer")]
266    use crate::Integer;
267    #[cfg(feature = "rational")]
268    use crate::Rational;
269    use crate::float;
270    use crate::float::{FreeCache, Special};
271    use crate::{Assign, Float};
272    use core::cmp::Ordering;
273    #[cfg(feature = "integer")]
274    use core::str::FromStr;
275
276    fn check_cmp_prim<T>(s: &[T], against: &[Float])
277    where
278        Float: Assign<T> + PartialEq<T> + PartialOrd<T>,
279        T: Copy + PartialEq<Float> + PartialOrd<Float>,
280    {
281        for op in s {
282            let fop = Float::with_val(150, *op);
283            for b in against {
284                assert_eq!(b.eq(op), PartialEq::<Float>::eq(b, &fop));
285                assert_eq!(op.eq(b), PartialEq::<Float>::eq(&fop, b));
286                assert_eq!(b.eq(op), op.eq(b));
287                assert_eq!(b.partial_cmp(op), PartialOrd::<Float>::partial_cmp(b, &fop));
288                assert_eq!(op.partial_cmp(b), PartialOrd::<Float>::partial_cmp(&fop, b));
289                assert_eq!(b.partial_cmp(op), op.partial_cmp(b).map(Ordering::reverse));
290            }
291        }
292    }
293
294    #[cfg(feature = "integer")]
295    fn check_cmp_big<'a, T>(s: &'a [T], against: &[Float])
296    where
297        Float: Assign<&'a T> + PartialEq<T> + PartialOrd<T>,
298        T: PartialEq<Float> + PartialOrd<Float>,
299    {
300        for op in s {
301            let fop = Float::with_val(150, op);
302            for b in against {
303                assert_eq!(b.eq(op), PartialEq::<Float>::eq(b, &fop));
304                assert_eq!(op.eq(b), PartialEq::<Float>::eq(&fop, b));
305                assert_eq!(b.eq(op), op.eq(b));
306                assert_eq!(b.partial_cmp(op), PartialOrd::<Float>::partial_cmp(b, &fop));
307                assert_eq!(op.partial_cmp(b), PartialOrd::<Float>::partial_cmp(&fop, b));
308                assert_eq!(b.partial_cmp(op), op.partial_cmp(b).map(Ordering::reverse));
309            }
310        }
311    }
312
313    #[test]
314    fn check_cmp_others() {
315        use crate::tests::{F32, F64, I32, I64, I128, U32, U64, U128};
316        let large = [
317            Float::with_val(20, Special::Zero),
318            Float::with_val(20, Special::NegZero),
319            Float::with_val(20, Special::Infinity),
320            Float::with_val(20, Special::NegInfinity),
321            Float::with_val(20, Special::Nan),
322            Float::with_val(20, 1),
323            Float::with_val(20, -1),
324            Float::with_val(20, 999_999e100),
325            Float::with_val(20, 999_999e-100),
326            Float::with_val(20, -999_999e100),
327            Float::with_val(20, -999_999e-100),
328        ];
329        #[cfg(feature = "integer")]
330        let z = &[
331            Integer::from(0),
332            Integer::from(1),
333            Integer::from(-1),
334            Integer::from_str("-1000000000000").unwrap(),
335            Integer::from_str("1000000000000").unwrap(),
336        ];
337        #[cfg(feature = "rational")]
338        let q = &[
339            Rational::from(0),
340            Rational::from(1),
341            Rational::from(-1),
342            Rational::from_str("-1000000000000/33333333333").unwrap(),
343            Rational::from_str("1000000000000/33333333333").unwrap(),
344        ];
345
346        let against = large
347            .iter()
348            .cloned()
349            .chain(U32.iter().map(|&x| Float::with_val(20, x)))
350            .chain(I32.iter().map(|&x| Float::with_val(20, x)))
351            .chain(U64.iter().map(|&x| Float::with_val(20, x)))
352            .chain(I64.iter().map(|&x| Float::with_val(20, x)))
353            .chain(U128.iter().map(|&x| Float::with_val(20, x)))
354            .chain(I128.iter().map(|&x| Float::with_val(20, x)))
355            .chain(F32.iter().map(|&x| Float::with_val(20, x)))
356            .chain(F64.iter().map(|&x| Float::with_val(20, x)))
357            .collect::<Vec<Float>>();
358        #[cfg(feature = "integer")]
359        let mut against = against;
360        #[cfg(feature = "integer")]
361        against.extend(z.iter().map(|x| Float::with_val(20, x)));
362        #[cfg(feature = "rational")]
363        against.extend(q.iter().map(|x| Float::with_val(20, x)));
364        check_cmp_prim(U32, &against);
365        check_cmp_prim(I32, &against);
366        check_cmp_prim(U64, &against);
367        check_cmp_prim(I64, &against);
368        check_cmp_prim(U128, &against);
369        check_cmp_prim(I128, &against);
370        check_cmp_prim(F32, &against);
371        check_cmp_prim(F64, &against);
372        #[cfg(feature = "integer")]
373        check_cmp_big(z, &against);
374        #[cfg(feature = "rational")]
375        check_cmp_big(q, &against);
376
377        float::free_cache(FreeCache::All);
378    }
379}