malachite_float/comparison/cmp.rs
1// Copyright © 2025 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::{ComparableFloat, ComparableFloatRef, Float};
11use core::cmp::Ordering::{self, *};
12
13impl PartialOrd for Float {
14 /// Compares two [`Float`]s.
15 ///
16 /// This implementation follows the IEEE 754 standard. `NaN` is not comparable to anything, not
17 /// even itself. Positive zero is equal to negative zero. [`Float`]s with different precisions
18 /// are equal if they represent the same numeric value.
19 ///
20 /// For different comparison behavior that provides a total order, consider using
21 /// [`ComparableFloat`] or [`ComparableFloatRef`].
22 ///
23 /// # Worst-case complexity
24 /// $T(n) = O(n)$
25 ///
26 /// $M(n) = O(1)$
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::{
34 /// Infinity, NaN, NegativeInfinity, NegativeOne, NegativeZero, One, OneHalf, Zero,
35 /// };
36 /// use malachite_float::Float;
37 /// use std::cmp::Ordering::*;
38 ///
39 /// assert_eq!(Float::NAN.partial_cmp(&Float::NAN), None);
40 /// assert_eq!(Float::ZERO.partial_cmp(&Float::NEGATIVE_ZERO), Some(Equal));
41 /// assert_eq!(Float::ONE.partial_cmp(&Float::one_prec(100)), Some(Equal));
42 /// assert!(Float::INFINITY > Float::ONE);
43 /// assert!(Float::NEGATIVE_INFINITY < Float::ONE);
44 /// assert!(Float::ONE_HALF < Float::ONE);
45 /// assert!(Float::ONE_HALF > Float::NEGATIVE_ONE);
46 /// ```
47 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
48 match (self, other) {
49 (float_nan!(), _) | (_, float_nan!()) => None,
50 (float_infinity!(), float_infinity!())
51 | (float_negative_infinity!(), float_negative_infinity!())
52 | (float_either_zero!(), float_either_zero!()) => Some(Equal),
53 (float_infinity!(), _) | (_, float_negative_infinity!()) => Some(Greater),
54 (float_negative_infinity!(), _) | (_, float_infinity!()) => Some(Less),
55 (Self(Finite { sign, .. }), float_either_zero!()) => {
56 Some(if *sign { Greater } else { Less })
57 }
58 (float_either_zero!(), Self(Finite { sign, .. })) => {
59 Some(if *sign { Less } else { Greater })
60 }
61 (
62 Self(Finite {
63 sign: s_x,
64 exponent: e_x,
65 significand: x,
66 ..
67 }),
68 Self(Finite {
69 sign: s_y,
70 exponent: e_y,
71 significand: y,
72 ..
73 }),
74 ) => Some(s_x.cmp(s_y).then_with(|| {
75 let abs_cmp = e_x.cmp(e_y).then_with(|| x.cmp_normalized_no_shift(y));
76 if *s_x { abs_cmp } else { abs_cmp.reverse() }
77 })),
78 }
79 }
80}
81
82impl<'a> Ord for ComparableFloatRef<'a> {
83 /// Compares two [`ComparableFloatRef`]s.
84 ///
85 /// This implementation does not follow the IEEE 754 standard. This is how
86 /// [`ComparableFloatRef`]s are ordered, least to greatest:
87 /// - $-\infty$
88 /// - Negative nonzero finite floats
89 /// - Negative zero
90 /// - NaN
91 /// - Positive zero
92 /// - Positive nonzero finite floats
93 /// - $\infty$
94 ///
95 /// When comparing two finite floats with the same numeric value but different precisions, the
96 /// one with greater precision is ordered to be further from zero.
97 ///
98 /// For different comparison behavior that follows the IEEE 754 standard, consider just using
99 /// [`Float`].
100 ///
101 /// # Worst-case complexity
102 /// $T(n) = O(n)$
103 ///
104 /// $M(n) = O(1)$
105 ///
106 /// where $T$ is time, $M$ is additional memory, and $n$ is `max(self.significant_bits(),
107 /// other.significant_bits())`.
108 ///
109 /// # Examples
110 /// ```
111 /// use malachite_base::num::basic::traits::{
112 /// Infinity, NaN, NegativeInfinity, NegativeOne, NegativeZero, One, OneHalf, Zero,
113 /// };
114 /// use malachite_float::{ComparableFloatRef, Float};
115 /// use std::cmp::Ordering::*;
116 ///
117 /// assert_eq!(
118 /// ComparableFloatRef(&Float::NAN).partial_cmp(&ComparableFloatRef(&Float::NAN)),
119 /// Some(Equal)
120 /// );
121 /// assert!(ComparableFloatRef(&Float::ZERO) > ComparableFloatRef(&Float::NEGATIVE_ZERO));
122 /// assert!(ComparableFloatRef(&Float::ONE) < ComparableFloatRef(&Float::one_prec(100)));
123 /// assert!(ComparableFloatRef(&Float::INFINITY) > ComparableFloatRef(&Float::ONE));
124 /// assert!(ComparableFloatRef(&Float::NEGATIVE_INFINITY) < ComparableFloatRef(&Float::ONE));
125 /// assert!(ComparableFloatRef(&Float::ONE_HALF) < ComparableFloatRef(&Float::ONE));
126 /// assert!(ComparableFloatRef(&Float::ONE_HALF) > ComparableFloatRef(&Float::NEGATIVE_ONE));
127 /// ```
128 fn cmp(&self, other: &Self) -> Ordering {
129 match (&self.0, &other.0) {
130 (float_nan!(), float_nan!())
131 | (float_infinity!(), float_infinity!())
132 | (float_negative_infinity!(), float_negative_infinity!()) => Equal,
133 (Float(Zero { sign: s_x }), Float(Zero { sign: s_y })) => s_x.cmp(s_y),
134 (float_infinity!(), _) | (_, float_negative_infinity!()) => Greater,
135 (float_negative_infinity!(), _) | (_, float_infinity!()) => Less,
136 (Float(NaN | Zero { .. }), Float(Finite { sign, .. }))
137 | (Float(NaN), Float(Zero { sign })) => {
138 if *sign {
139 Less
140 } else {
141 Greater
142 }
143 }
144 (Float(Finite { sign, .. } | Zero { sign }), Float(NaN))
145 | (Float(Finite { sign, .. }), Float(Zero { .. })) => {
146 if *sign {
147 Greater
148 } else {
149 Less
150 }
151 }
152 (
153 Float(Finite {
154 sign: s_x,
155 exponent: e_x,
156 precision: p_x,
157 significand: x,
158 }),
159 Float(Finite {
160 sign: s_y,
161 exponent: e_y,
162 precision: p_y,
163 significand: y,
164 }),
165 ) => s_x.cmp(s_y).then_with(|| {
166 let abs_cmp = e_x
167 .cmp(e_y)
168 .then_with(|| x.cmp_normalized_no_shift(y))
169 .then_with(|| p_x.cmp(p_y));
170 if *s_x { abs_cmp } else { abs_cmp.reverse() }
171 }),
172 }
173 }
174}
175
176impl PartialOrd for ComparableFloatRef<'_> {
177 /// Compares two [`ComparableFloatRef`]s.
178 ///
179 /// See the documentation for the [`Ord`] implementation.
180 #[inline]
181 fn partial_cmp(&self, other: &ComparableFloatRef) -> Option<Ordering> {
182 Some(self.cmp(other))
183 }
184}
185
186impl Ord for ComparableFloat {
187 /// Compares two [`ComparableFloat`]s.
188 ///
189 /// This implementation does not follow the IEEE 754 standard. This is how [`ComparableFloat`]s
190 /// are ordered, least to greatest:
191 /// - $-\infty$
192 /// - Negative nonzero finite floats
193 /// - Negative zero
194 /// - NaN
195 /// - Positive zero
196 /// - Positive nonzero finite floats
197 /// - $\infty$
198 ///
199 /// When comparing two finite floats with the same numeric value but different precisions, the
200 /// one with greater precision is ordered to be further from zero.
201 ///
202 /// For different comparison behavior that follows the IEEE 754 standard, consider just using
203 /// [`Float`].
204 ///
205 /// # Worst-case complexity
206 /// $T(n) = O(n)$
207 ///
208 /// $M(n) = O(1)$
209 ///
210 /// where $T$ is time, $M$ is additional memory, and $n$ is `max(self.significant_bits(),
211 /// other.significant_bits())`.
212 ///
213 /// # Examples
214 /// ```
215 /// use malachite_base::num::basic::traits::{
216 /// Infinity, NaN, NegativeInfinity, NegativeOne, NegativeZero, One, OneHalf, Zero,
217 /// };
218 /// use malachite_float::{ComparableFloat, Float};
219 /// use std::cmp::Ordering::*;
220 ///
221 /// assert_eq!(
222 /// ComparableFloat(Float::NAN).partial_cmp(&ComparableFloat(Float::NAN)),
223 /// Some(Equal)
224 /// );
225 /// assert!(ComparableFloat(Float::ZERO) > ComparableFloat(Float::NEGATIVE_ZERO));
226 /// assert!(ComparableFloat(Float::ONE) < ComparableFloat(Float::one_prec(100)));
227 /// assert!(ComparableFloat(Float::INFINITY) > ComparableFloat(Float::ONE));
228 /// assert!(ComparableFloat(Float::NEGATIVE_INFINITY) < ComparableFloat(Float::ONE));
229 /// assert!(ComparableFloat(Float::ONE_HALF) < ComparableFloat(Float::ONE));
230 /// assert!(ComparableFloat(Float::ONE_HALF) > ComparableFloat(Float::NEGATIVE_ONE));
231 /// ```
232 #[inline]
233 fn cmp(&self, other: &Self) -> Ordering {
234 self.as_ref().cmp(&other.as_ref())
235 }
236}
237
238#[allow(clippy::non_canonical_partial_ord_impl)]
239impl PartialOrd for ComparableFloat {
240 /// Compares two [`ComparableFloat`]s.
241 ///
242 /// See the documentation for the [`Ord`] implementation.
243 #[inline]
244 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
245 Some(self.as_ref().cmp(&other.as_ref()))
246 }
247}