malachite_float/float/comparison/cmp_abs.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::{ComparableFloat, ComparableFloatRef, Float};
11use core::cmp::Ordering::{self, *};
12use malachite_base::num::comparison::traits::{OrdAbs, PartialOrdAbs, PartialOrdAbsDouble};
13
14impl PartialOrdAbs for Float {
15 /// Compares the absolute values of two [`Float`]s.
16 ///
17 /// This implementation follows the IEEE 754 standard. `NaN` is not comparable to anything, not
18 /// even itself. [`Float`]s with different precisions are equal if they represent the same
19 /// numeric value.
20 ///
21 /// For different comparison behavior that provides a total order, consider using
22 /// [`ComparableFloat`] or [`ComparableFloatRef`].
23 ///
24 /// # Worst-case complexity
25 /// $T(n) = O(n)$
26 ///
27 /// $M(n) = O(1)$
28 ///
29 /// where $T$ is time, $M$ is additional memory, and $n$ is `max(self.significant_bits(),
30 /// other.significant_bits())`.
31 ///
32 /// # Examples
33 /// ```
34 /// use malachite_base::num::basic::traits::{
35 /// Infinity, NaN, NegativeInfinity, NegativeOne, NegativeZero, One, OneHalf, Zero,
36 /// };
37 /// use malachite_base::num::comparison::traits::PartialOrdAbs;
38 /// use malachite_float::Float;
39 /// use std::cmp::Ordering::*;
40 ///
41 /// assert_eq!(Float::NAN.partial_cmp_abs(&Float::NAN), None);
42 /// assert_eq!(
43 /// Float::ZERO.partial_cmp_abs(&Float::NEGATIVE_ZERO),
44 /// Some(Equal)
45 /// );
46 /// assert_eq!(
47 /// Float::ONE.partial_cmp_abs(&Float::one_prec(100)),
48 /// Some(Equal)
49 /// );
50 /// assert!(Float::INFINITY.gt_abs(&Float::ONE));
51 /// assert!(Float::NEGATIVE_INFINITY.gt_abs(&Float::ONE));
52 /// assert!(Float::ONE_HALF.lt_abs(&Float::ONE));
53 /// assert!(Float::ONE_HALF.lt_abs(&Float::NEGATIVE_ONE));
54 /// ```
55 fn partial_cmp_abs(&self, other: &Self) -> Option<Ordering> {
56 match (self, other) {
57 (float_nan!(), _) | (_, float_nan!()) => None,
58 (float_either_infinity!(), float_either_infinity!())
59 | (float_either_zero!(), float_either_zero!()) => Some(Equal),
60 (float_either_infinity!(), _) | (_, float_either_zero!()) => Some(Greater),
61 (_, float_either_infinity!()) | (float_either_zero!(), _) => Some(Less),
62 (
63 Self(Finite {
64 exponent: e_x,
65 significand: x,
66 ..
67 }),
68 Self(Finite {
69 exponent: e_y,
70 significand: y,
71 ..
72 }),
73 ) => Some(e_x.cmp(e_y).then_with(|| x.cmp_normalized_no_shift(y))),
74 }
75 }
76}
77
78impl<'a> OrdAbs for ComparableFloatRef<'a> {
79 /// Compares the absolute values of two [`ComparableFloatRef`]s.
80 ///
81 /// This implementation does not follow the IEEE 754 standard. This is how
82 /// [`ComparableFloatRef`]s are ordered by absolute value, from least to greatest:
83 /// - NaN
84 /// - Positive and negative zero
85 /// - Nonzero finite floats
86 /// - $\infty$ and $-\infty$
87 ///
88 /// For different comparison behavior that follows the IEEE 754 standard, consider just using
89 /// [`Float`].
90 ///
91 /// # Worst-case complexity
92 /// $T(n) = O(n)$
93 ///
94 /// $M(n) = O(1)$
95 ///
96 /// where $T$ is time, $M$ is additional memory, and $n$ is `max(self.significant_bits(),
97 /// other.significant_bits())`.
98 ///
99 /// # Examples
100 /// ```
101 /// use malachite_base::num::basic::traits::{
102 /// Infinity, NaN, NegativeInfinity, NegativeOne, NegativeZero, One, OneHalf, Zero,
103 /// };
104 /// use malachite_base::num::comparison::traits::PartialOrdAbs;
105 /// use malachite_float::{ComparableFloatRef, Float};
106 /// use std::cmp::Ordering::*;
107 ///
108 /// assert_eq!(
109 /// ComparableFloatRef(&Float::NAN).partial_cmp_abs(&ComparableFloatRef(&Float::NAN)),
110 /// Some(Equal)
111 /// );
112 /// assert_eq!(
113 /// ComparableFloatRef(&Float::ZERO)
114 /// .partial_cmp_abs(&ComparableFloatRef(&Float::NEGATIVE_ZERO)),
115 /// Some(Equal)
116 /// );
117 /// assert!(ComparableFloatRef(&Float::ONE).lt_abs(&ComparableFloatRef(&Float::one_prec(100))));
118 /// assert!(ComparableFloatRef(&Float::INFINITY).gt_abs(&ComparableFloatRef(&Float::ONE)));
119 /// assert!(
120 /// ComparableFloatRef(&Float::NEGATIVE_INFINITY).gt_abs(&ComparableFloatRef(&Float::ONE))
121 /// );
122 /// assert!(ComparableFloatRef(&Float::ONE_HALF).lt_abs(&ComparableFloatRef(&Float::ONE)));
123 /// assert!(
124 /// ComparableFloatRef(&Float::ONE_HALF).lt_abs(&ComparableFloatRef(&Float::NEGATIVE_ONE))
125 /// );
126 /// ```
127 #[allow(clippy::match_same_arms)]
128 fn cmp_abs(&self, other: &Self) -> Ordering {
129 match (&self.0, &other.0) {
130 (float_nan!(), float_nan!())
131 | (float_either_infinity!(), float_either_infinity!())
132 | (float_either_zero!(), float_either_zero!()) => Equal,
133 (float_either_infinity!(), _) | (_, float_nan!()) => Greater,
134 (_, float_either_infinity!()) | (float_nan!(), _) => Less,
135 (float_either_zero!(), _) => Less,
136 (_, float_either_zero!()) => Greater,
137 (
138 Float(Finite {
139 exponent: e_x,
140 precision: p_x,
141 significand: x,
142 ..
143 }),
144 Float(Finite {
145 exponent: e_y,
146 precision: p_y,
147 significand: y,
148 ..
149 }),
150 ) => e_x
151 .cmp(e_y)
152 .then_with(|| x.cmp_normalized_no_shift(y))
153 .then_with(|| p_x.cmp(p_y)),
154 }
155 }
156}
157
158impl PartialOrdAbs for ComparableFloatRef<'_> {
159 /// Compares the absolute values of two [`ComparableFloatRef`]s.
160 ///
161 /// See the documentation for the [`Ord`] implementation.
162 #[inline]
163 fn partial_cmp_abs(&self, other: &ComparableFloatRef) -> Option<Ordering> {
164 Some(self.cmp_abs(other))
165 }
166}
167
168impl OrdAbs for ComparableFloat {
169 /// Compares the absolute values of two [`ComparableFloat`]s.
170 ///
171 /// This implementation does not follow the IEEE 754 standard. This is how [`ComparableFloat`]s
172 /// are ordered by absolute value, from least to greatest:
173 /// - NaN
174 /// - Positive and negative zero
175 /// - Nonzero finite floats
176 /// - $\infty$ and $-\infty$
177 ///
178 /// For different comparison behavior that follows the IEEE 754 standard, consider just using
179 /// [`Float`].
180 ///
181 /// # Worst-case complexity
182 /// $T(n) = O(n)$
183 ///
184 /// $M(n) = O(1)$
185 ///
186 /// where $T$ is time, $M$ is additional memory, and $n$ is `max(self.significant_bits(),
187 /// other.significant_bits())`.
188 ///
189 /// # Examples
190 /// ```
191 /// use malachite_base::num::basic::traits::{
192 /// Infinity, NaN, NegativeInfinity, NegativeOne, NegativeZero, One, OneHalf, Zero,
193 /// };
194 /// use malachite_base::num::comparison::traits::PartialOrdAbs;
195 /// use malachite_float::{ComparableFloat, Float};
196 /// use std::cmp::Ordering::*;
197 ///
198 /// assert_eq!(
199 /// ComparableFloat(Float::NAN).partial_cmp_abs(&ComparableFloat(Float::NAN)),
200 /// Some(Equal)
201 /// );
202 /// assert_eq!(
203 /// ComparableFloat(Float::ZERO).partial_cmp_abs(&ComparableFloat(Float::NEGATIVE_ZERO)),
204 /// Some(Equal)
205 /// );
206 /// assert!(ComparableFloat(Float::ONE).lt_abs(&ComparableFloat(Float::one_prec(100))));
207 /// assert!(ComparableFloat(Float::INFINITY).gt_abs(&ComparableFloat(Float::ONE)));
208 /// assert!(ComparableFloat(Float::NEGATIVE_INFINITY).gt_abs(&ComparableFloat(Float::ONE)));
209 /// assert!(ComparableFloat(Float::ONE_HALF).lt_abs(&ComparableFloat(Float::ONE)));
210 /// assert!(ComparableFloat(Float::ONE_HALF).lt_abs(&ComparableFloat(Float::NEGATIVE_ONE)));
211 /// ```
212 #[inline]
213 fn cmp_abs(&self, other: &Self) -> Ordering {
214 self.as_ref().cmp_abs(&other.as_ref())
215 }
216}
217
218impl PartialOrdAbs for ComparableFloat {
219 /// Compares the absolute values of two [`ComparableFloatRef`]s.
220 ///
221 /// See the documentation for the [`Ord`] implementation.
222 #[inline]
223 fn partial_cmp_abs(&self, other: &Self) -> Option<Ordering> {
224 Some(self.as_ref().cmp_abs(&other.as_ref()))
225 }
226}
227
228impl PartialOrdAbsDouble for Float {
229 /// Compares the absolute value of a [`Float`] with twice the absolute value of another
230 /// [`Float`].
231 ///
232 /// The doubled value is never formed, so the comparison is right even when doubling would
233 /// overflow: doubling a [`Float`] raises its exponent, and an exponent past the maximum becomes
234 /// an infinity, which would compare equal to an infinite `self` rather than less than it. Here
235 /// the exponents are compared directly instead.
236 ///
237 /// This implementation follows the IEEE 754 standard. `NaN` is not comparable to anything, not
238 /// even itself. [`Float`]s with different precisions are equal if they represent the same
239 /// numeric value.
240 ///
241 /// $$
242 /// f(x, y) = \operatorname{cmp}(|x|, 2|y|).
243 /// $$
244 ///
245 /// # Worst-case complexity
246 /// $T(n) = O(n)$
247 ///
248 /// $M(n) = O(1)$
249 ///
250 /// where $T$ is time, $M$ is additional memory, and $n$ is `max(self.significant_bits(),
251 /// other.significant_bits())`.
252 ///
253 /// # Examples
254 /// ```
255 /// use malachite_base::num::basic::traits::{Infinity, NaN, One, OneHalf, Zero};
256 /// use malachite_base::num::comparison::traits::PartialOrdAbsDouble;
257 /// use malachite_float::Float;
258 /// use std::cmp::Ordering::*;
259 ///
260 /// assert_eq!(Float::NAN.partial_cmp_abs_double(&Float::ONE), None);
261 /// assert_eq!(
262 /// Float::ONE.partial_cmp_abs_double(&Float::ONE_HALF),
263 /// Some(Equal)
264 /// );
265 /// assert_eq!(
266 /// Float::ONE_HALF.partial_cmp_abs_double(&Float::ONE),
267 /// Some(Less)
268 /// );
269 /// // doubling the largest finite value would overflow to an infinity, but an infinity is
270 /// // still the larger
271 /// assert_eq!(
272 /// Float::INFINITY.partial_cmp_abs_double(&Float::max_finite_value_with_prec(10)),
273 /// Some(Greater)
274 /// );
275 /// assert_eq!(
276 /// Float::ZERO.partial_cmp_abs_double(&Float::ZERO),
277 /// Some(Equal)
278 /// );
279 /// ```
280 fn partial_cmp_abs_double(&self, other: &Self) -> Option<Ordering> {
281 match (self, other) {
282 (float_nan!(), _) | (_, float_nan!()) => None,
283 // doubling leaves an infinity and a zero alone
284 (float_either_infinity!(), float_either_infinity!())
285 | (float_either_zero!(), float_either_zero!()) => Some(Equal),
286 (float_either_infinity!(), _) | (_, float_either_zero!()) => Some(Greater),
287 (_, float_either_infinity!()) | (float_either_zero!(), _) => Some(Less),
288 (
289 Self(Finite {
290 exponent: e_x,
291 significand: x,
292 ..
293 }),
294 Self(Finite {
295 exponent: e_y,
296 significand: y,
297 ..
298 }),
299 ) => {
300 // Doubling raises the exponent by one; the exponent range leaves room for that even
301 // at the maximum, since it is far from `i32`'s.
302 Some(
303 e_x.cmp(&(e_y + 1))
304 .then_with(|| x.cmp_normalized_no_shift(y)),
305 )
306 }
307 }
308 }
309}