Skip to main content

malachite_float/float/comparison/
partial_cmp_rational.rs

1// Copyright © 2026 Mikhail Hogrefe
2//
3// This file is part of Malachite.
4//
5// Malachite is free software: you can redistribute it and/or modify it under the terms of the GNU
6// Lesser General Public License (LGPL) as published by the Free Software Foundation; either version
7// 3 of the License, or (at your option) any later version. See <https://www.gnu.org/licenses/>.
8
9use crate::InnerFloat::{Finite, Infinity, NaN, Zero};
10use crate::{Float, significand_bits};
11use core::cmp::Ordering::{self, *};
12use malachite_base::num::arithmetic::traits::Sign;
13use malachite_base::num::conversion::traits::ExactFrom;
14use malachite_q::Rational;
15
16impl PartialOrd<Rational> for Float {
17    /// Compares a [`Float`] to a [`Rational`].
18    ///
19    /// NaN is not comparable to any [`Rational`]. $\infty$ is greater than any [`Rational`], and
20    /// $-\infty$ is less. Both the [`Float`] zero and the [`Float`] negative zero are equal to the
21    /// [`Rational`] zero.
22    ///
23    /// # Worst-case complexity
24    /// $T(n) = O(n \log n \log\log n)$
25    ///
26    /// $M(n) = O(n \log n)$
27    ///
28    /// where $T$ is time, $M$ is additional memory, and $n$ is `max(self.significant_bits(),
29    /// other.significant_bits())`.
30    ///
31    /// # Examples
32    /// ```
33    /// use malachite_base::num::basic::traits::{Infinity, NegativeInfinity};
34    /// use malachite_float::Float;
35    /// use malachite_q::Rational;
36    ///
37    /// assert!(Float::from(80) < Rational::from(100));
38    /// assert!(Float::from(-80) > Rational::from(-100));
39    /// assert!(Float::INFINITY > Rational::from(100));
40    /// assert!(Float::NEGATIVE_INFINITY < Rational::from(-100));
41    /// assert!(Float::from(1.0f64 / 3.0) < Rational::from_unsigneds(1u8, 3));
42    /// ```
43    // The exact exponent comparison above already decides every case the screens of `cmp_product`
44    // could, so the final cross-multiplication is formed directly.
45    #[cfg_attr(dylint_lib = "malachite_lints", allow(use_cmp_product))]
46    fn partial_cmp(&self, other: &Rational) -> Option<Ordering> {
47        match (self, other) {
48            (float_nan!(), _) => None,
49            (float_infinity!(), _) => Some(Greater),
50            (float_negative_infinity!(), _) => Some(Less),
51            (float_either_zero!(), y) => 0u32.partial_cmp(y),
52            (
53                Self(Finite {
54                    sign: s_x,
55                    exponent: e_x,
56                    significand: significand_x,
57                    ..
58                }),
59                y,
60            ) => Some(if *y == 0u32 {
61                if *s_x { Greater } else { Less }
62            } else {
63                let s_cmp = s_x.cmp(&(*y > 0));
64                if s_cmp != Equal {
65                    return Some(s_cmp);
66                }
67                let e_x = i64::from(*e_x);
68                let exp_cmp = (e_x - 1).cmp(&other.floor_log_base_2_abs());
69                if exp_cmp != Equal {
70                    return Some(if *s_x { exp_cmp } else { exp_cmp.reverse() });
71                }
72                let shift = e_x - i64::exact_from(significand_bits(significand_x));
73                let abs_shift = shift.unsigned_abs();
74                let prod_cmp = match shift.sign() {
75                    Equal => (significand_x * other.denominator_ref()).cmp(other.numerator_ref()),
76                    Greater => ((significand_x * other.denominator_ref()) << abs_shift)
77                        .cmp(other.numerator_ref()),
78                    Less => {
79                        let n_trailing_zeros = significand_x.trailing_zeros().unwrap();
80                        if abs_shift <= n_trailing_zeros {
81                            ((significand_x >> abs_shift) * other.denominator_ref())
82                                .cmp(other.numerator_ref())
83                        } else {
84                            ((significand_x >> n_trailing_zeros) * other.denominator_ref())
85                                .cmp(&(other.numerator_ref() << (abs_shift - n_trailing_zeros)))
86                        }
87                    }
88                };
89                if *s_x { prod_cmp } else { prod_cmp.reverse() }
90            }),
91        }
92    }
93}
94
95impl PartialOrd<Float> for Rational {
96    /// Compares an [`Rational`] to a [`Float`].
97    ///
98    /// No [`Rational`] is comparable to NaN. Every [`Rational`] is smaller than $\infty$ and
99    /// greater than $-\infty$. The [`Rational`] zero is equal to both the [`Float`] zero and the
100    /// [`Float`] negative zero.
101    ///
102    /// # Worst-case complexity
103    /// $T(n) = O(n \log n \log\log n)$
104    ///
105    /// $M(n) = O(n \log n)$
106    ///
107    /// where $T$ is time, $M$ is additional memory, and $n$ is `max(self.significant_bits(),
108    /// other.significant_bits())`.
109    ///
110    /// # Examples
111    /// ```
112    /// use malachite_base::num::basic::traits::{Infinity, NegativeInfinity};
113    /// use malachite_float::Float;
114    /// use malachite_q::Rational;
115    ///
116    /// assert!(Rational::from(100) > Float::from(80));
117    /// assert!(Rational::from(-100) < Float::from(-80));
118    /// assert!(Rational::from(100) < Float::INFINITY);
119    /// assert!(Rational::from(-100) > Float::NEGATIVE_INFINITY);
120    /// assert!(Rational::from_unsigneds(1u8, 3) > Float::from(1.0f64 / 3.0));
121    /// ```
122    #[inline]
123    fn partial_cmp(&self, other: &Float) -> Option<Ordering> {
124        other.partial_cmp(self).map(Ordering::reverse)
125    }
126}