dashu_float/
cmp.rs

1use core::cmp::Ordering;
2
3use dashu_base::{AbsOrd, EstimatedLog2, Sign};
4use dashu_int::{IBig, UBig};
5
6use crate::{
7    fbig::FBig,
8    repr::Repr,
9    repr::Word,
10    round::Round,
11    utils::{shl_digits, shl_digits_in_place},
12};
13
14impl<R1: Round, R2: Round, const B: Word> PartialEq<FBig<R2, B>> for FBig<R1, B> {
15    #[inline]
16    fn eq(&self, other: &FBig<R2, B>) -> bool {
17        match (self.repr.is_infinite(), other.repr.is_infinite()) {
18            // +inf == +inf, -inf == -inf
19            (true, true) => !((self.repr.exponent >= 0) ^ (other.repr.exponent >= 0)),
20
21            // the representation is normalized so direct comparing is okay,
22            // and the context doesn't count in comparison
23            (false, false) => self.repr == other.repr,
24
25            // inf != any exact numbers
26            (_, _) => false,
27        }
28    }
29}
30impl<R: Round, const B: Word> Eq for FBig<R, B> {}
31
32fn repr_cmp_same_base<const B: Word, const ABS: bool>(
33    lhs: &Repr<B>,
34    rhs: &Repr<B>,
35    precision: Option<(usize, usize)>,
36) -> Ordering {
37    // case 1: compare with inf
38    match (lhs.is_infinite(), rhs.is_infinite()) {
39        (true, true) => {
40            return if ABS {
41                Ordering::Equal
42            } else {
43                lhs.exponent.cmp(&rhs.exponent)
44            }
45        }
46        (false, true) => {
47            return match ABS || rhs.exponent >= 0 {
48                true => Ordering::Less,
49                false => Ordering::Greater,
50            }
51        }
52        (true, false) => {
53            return match ABS || lhs.exponent >= 0 {
54                true => Ordering::Greater,
55                false => Ordering::Less,
56            }
57        }
58        _ => {}
59    };
60
61    // case 2: compare sign
62    let sign = if ABS {
63        Sign::Positive
64    } else {
65        match (lhs.significand.sign(), rhs.significand.sign()) {
66            (Sign::Positive, Sign::Positive) => Sign::Positive,
67            (Sign::Positive, Sign::Negative) => return Ordering::Greater,
68            (Sign::Negative, Sign::Positive) => return Ordering::Less,
69            (Sign::Negative, Sign::Negative) => Sign::Negative,
70        }
71    };
72
73    // case 3: compare with 0
74    match (lhs.is_zero(), rhs.is_zero()) {
75        (true, true) => return Ordering::Equal,
76        (true, false) => {
77            // rhs must be positive, otherwise case 2 will return
78            return Ordering::Less;
79        }
80        (false, true) => {
81            // lhs must be positive, otherwise case 2 will return
82            return Ordering::Greater;
83        }
84        _ => {}
85    }
86
87    // case 4: compare exponent and precision
88    let (lhs_exp, rhs_exp) = (lhs.exponent, rhs.exponent);
89    if let Some((lhs_prec, rhs_prec)) = precision {
90        // only compare when both number are not having arbitrary precision
91        if lhs_prec != 0 && rhs_prec != 0 {
92            if lhs_exp > rhs_exp + rhs_prec as isize {
93                return sign * Ordering::Greater;
94            }
95            if rhs_exp > lhs_exp + lhs_prec as isize {
96                return sign * Ordering::Less;
97            }
98        }
99    }
100
101    // case 5: compare exponent and digits
102    let (lhs_digits, rhs_digits) = (lhs.digits_ub(), rhs.digits_ub());
103    if lhs_exp > rhs_exp + rhs_digits as isize {
104        return sign * Ordering::Greater;
105    }
106    if rhs_exp > lhs_exp + lhs_digits as isize {
107        return sign * Ordering::Less;
108    }
109
110    // case 6: compare exact values by shifting
111    let (lhs_signif, rhs_signif) = (&lhs.significand, &rhs.significand);
112    if ABS {
113        match lhs_exp.cmp(&rhs_exp) {
114            Ordering::Equal => lhs_signif.abs_cmp(rhs_signif),
115            Ordering::Greater => {
116                shl_digits::<B>(lhs_signif, (lhs_exp - rhs_exp) as usize).abs_cmp(rhs_signif)
117            }
118            Ordering::Less => {
119                lhs_signif.abs_cmp(&shl_digits::<B>(rhs_signif, (rhs_exp - lhs_exp) as usize))
120            }
121        }
122    } else {
123        match lhs_exp.cmp(&rhs_exp) {
124            Ordering::Equal => lhs_signif.cmp(rhs_signif),
125            Ordering::Greater => {
126                shl_digits::<B>(lhs_signif, (lhs_exp - rhs_exp) as usize).cmp(rhs_signif)
127            }
128            Ordering::Less => {
129                lhs_signif.cmp(&shl_digits::<B>(rhs_signif, (rhs_exp - lhs_exp) as usize))
130            }
131        }
132    }
133}
134
135impl<const B: Word> PartialOrd for Repr<B> {
136    #[inline]
137    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
138        Some(self.cmp(other))
139    }
140}
141
142impl<const B: Word> Ord for Repr<B> {
143    #[inline]
144    fn cmp(&self, other: &Self) -> Ordering {
145        repr_cmp_same_base::<B, false>(self, other, None)
146    }
147}
148
149impl<R1: Round, R2: Round, const B: Word> PartialOrd<FBig<R2, B>> for FBig<R1, B> {
150    #[inline]
151    fn partial_cmp(&self, other: &FBig<R2, B>) -> Option<Ordering> {
152        Some(repr_cmp_same_base::<B, false>(
153            &self.repr,
154            &other.repr,
155            Some((self.context.precision, other.context.precision)),
156        ))
157    }
158}
159
160impl<R: Round, const B: Word> Ord for FBig<R, B> {
161    #[inline]
162    fn cmp(&self, other: &Self) -> Ordering {
163        repr_cmp_same_base::<B, false>(
164            &self.repr,
165            &other.repr,
166            Some((self.context.precision, other.context.precision)),
167        )
168    }
169}
170
171impl<R: Round, const B: Word> AbsOrd for FBig<R, B> {
172    #[inline]
173    fn abs_cmp(&self, other: &Self) -> Ordering {
174        repr_cmp_same_base::<B, true>(
175            &self.repr,
176            &other.repr,
177            Some((self.context.precision, other.context.precision)),
178        )
179    }
180}
181
182pub(crate) fn repr_cmp_ubig<const B: Word, const ABS: bool>(lhs: &Repr<B>, rhs: &UBig) -> Ordering {
183    // case 1: compare with inf
184    if lhs.is_infinite() {
185        return if lhs.exponent > 0 || ABS {
186            Ordering::Greater
187        } else {
188            Ordering::Less
189        };
190    }
191
192    // case 2: compare sign
193    if !ABS && lhs.significand.sign() == Sign::Negative {
194        return Ordering::Less;
195    }
196
197    // case 3: compare log2 estimations
198    let (lhs_lo, lhs_hi) = lhs.log2_bounds();
199    let (rhs_lo, rhs_hi) = rhs.log2_bounds();
200    if lhs_lo > rhs_hi {
201        return Ordering::Greater;
202    }
203    if lhs_hi < rhs_lo {
204        return Ordering::Less;
205    }
206
207    // case 4: compare the exact values
208    let mut rhs: IBig = rhs.clone().into();
209    if lhs.exponent < 0 {
210        shl_digits_in_place::<B>(&mut rhs, (-lhs.exponent) as usize);
211        lhs.significand.cmp(&rhs)
212    } else {
213        shl_digits::<B>(&lhs.significand, lhs.exponent as usize).cmp(&rhs)
214    }
215}
216
217pub(crate) fn repr_cmp_ibig<const B: Word, const ABS: bool>(lhs: &Repr<B>, rhs: &IBig) -> Ordering {
218    // case 1: compare with inf
219    if lhs.is_infinite() {
220        return if lhs.exponent > 0 || ABS {
221            Ordering::Greater
222        } else {
223            Ordering::Less
224        };
225    }
226
227    // case 2: compare sign
228    let sign = if ABS {
229        Sign::Positive
230    } else {
231        match (lhs.significand.sign(), rhs.sign()) {
232            (Sign::Positive, Sign::Positive) => Sign::Positive,
233            (Sign::Positive, Sign::Negative) => return Ordering::Greater,
234            (Sign::Negative, Sign::Positive) => return Ordering::Less,
235            (Sign::Negative, Sign::Negative) => Sign::Negative,
236        }
237    };
238
239    // case 3: compare log2 estimations
240    let (lhs_lo, lhs_hi) = lhs.log2_bounds();
241    let (rhs_lo, rhs_hi) = rhs.log2_bounds();
242    if lhs_lo > rhs_hi {
243        return sign * Ordering::Greater;
244    }
245    if lhs_hi < rhs_lo {
246        return sign * Ordering::Less;
247    }
248
249    // case 4: compare the exact values
250    if lhs.exponent < 0 {
251        lhs.significand
252            .cmp(&shl_digits::<B>(rhs, (-lhs.exponent) as usize))
253    } else {
254        shl_digits::<B>(&lhs.significand, lhs.exponent as usize).cmp(rhs)
255    }
256}
257
258macro_rules! impl_abs_ord_with_method {
259    ($T:ty, $method:ident) => {
260        impl<const B: Word> AbsOrd<$T> for Repr<B> {
261            #[inline]
262            fn abs_cmp(&self, other: &$T) -> Ordering {
263                $method::<B, true>(self, other)
264            }
265        }
266        impl<const B: Word> AbsOrd<Repr<B>> for $T {
267            #[inline]
268            fn abs_cmp(&self, other: &Repr<B>) -> Ordering {
269                $method::<B, true>(other, self).reverse()
270            }
271        }
272        impl<R: Round, const B: Word> AbsOrd<$T> for FBig<R, B> {
273            #[inline]
274            fn abs_cmp(&self, other: &$T) -> Ordering {
275                $method::<B, true>(&self.repr, other)
276            }
277        }
278        impl<R: Round, const B: Word> AbsOrd<FBig<R, B>> for $T {
279            #[inline]
280            fn abs_cmp(&self, other: &FBig<R, B>) -> Ordering {
281                $method::<B, true>(&other.repr, self).reverse()
282            }
283        }
284    };
285}
286impl_abs_ord_with_method!(UBig, repr_cmp_ubig);
287impl_abs_ord_with_method!(IBig, repr_cmp_ibig);