malachite_float/comparison/cmp_abs.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, *};
12use malachite_base::num::comparison::traits::{OrdAbs, PartialOrdAbs};
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: &Float) -> 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 Float(Finite {
64 exponent: e_x,
65 significand: x,
66 ..
67 }),
68 Float(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: &ComparableFloatRef<'a>) -> 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: &ComparableFloat) -> 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: &ComparableFloat) -> Option<Ordering> {
224 Some(self.as_ref().cmp_abs(&other.as_ref()))
225 }
226}