malachite_float/arithmetic/
shr_round.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::Float;
10use crate::InnerFloat::Finite;
11use core::cmp::Ordering::{self, *};
12use malachite_base::num::arithmetic::traits::{IsPowerOf2, ShrRound, ShrRoundAssign};
13use malachite_base::num::basic::integers::PrimitiveInt;
14use malachite_base::num::basic::traits::{Infinity, NegativeInfinity, NegativeZero, Zero};
15use malachite_base::num::conversion::traits::WrappingFrom;
16use malachite_base::num::logic::traits::SignificantBits;
17use malachite_base::rounding_modes::RoundingMode::{self, *};
18
19pub(crate) fn shr_prec_round_assign_helper<T: PrimitiveInt>(
20    x: &mut Float,
21    bits: T,
22    prec: u64,
23    rm: RoundingMode,
24    previous_o: Ordering,
25) -> Ordering
26where
27    i32: WrappingFrom<T>,
28{
29    if let Float(Finite {
30        significand,
31        exponent,
32        sign,
33        precision,
34    }) = x
35    {
36        let mut possibly_just_under_min = false;
37        if let Ok(bits) = bits.try_into() {
38            if let Some(new_exponent) = exponent.checked_sub(bits) {
39                possibly_just_under_min = true;
40                if (Float::MIN_EXPONENT..=Float::MAX_EXPONENT).contains(&new_exponent) {
41                    *exponent = new_exponent;
42                    return previous_o;
43                }
44            }
45        }
46        assert!(rm != Exact, "Inexact Float right-shift");
47        if bits < T::ZERO {
48            match (*sign, rm) {
49                (true, Up | Ceiling | Nearest) => {
50                    *x = Float::INFINITY;
51                    Greater
52                }
53                (true, Floor | Down) => {
54                    *x = Float::max_finite_value_with_prec(prec);
55                    Less
56                }
57                (false, Up | Floor | Nearest) => {
58                    *x = Float::NEGATIVE_INFINITY;
59                    Less
60                }
61                (false, Ceiling | Down) => {
62                    *x = -Float::max_finite_value_with_prec(prec);
63                    Greater
64                }
65                (_, Exact) => unreachable!(),
66            }
67        } else if rm == Nearest
68            && possibly_just_under_min
69            && *exponent - i32::wrapping_from(bits) == Float::MIN_EXPONENT - 1
70            && (previous_o == if *sign { Less } else { Greater } || !significand.is_power_of_2())
71        {
72            if *sign {
73                *x = Float::min_positive_value_prec(*precision);
74                Greater
75            } else {
76                *x = -Float::min_positive_value_prec(*precision);
77                Less
78            }
79        } else {
80            match (*sign, rm) {
81                (true, Up | Ceiling) => {
82                    *x = Float::min_positive_value_prec(prec);
83                    Greater
84                }
85                (true, Floor | Down | Nearest) => {
86                    *x = Float::ZERO;
87                    Less
88                }
89                (false, Up | Floor) => {
90                    *x = -Float::min_positive_value_prec(prec);
91                    Less
92                }
93                (false, Ceiling | Down | Nearest) => {
94                    *x = Float::NEGATIVE_ZERO;
95                    Greater
96                }
97                (_, Exact) => unreachable!(),
98            }
99        }
100    } else {
101        Equal
102    }
103}
104
105fn shr_round_primitive_int_ref<T: PrimitiveInt>(
106    x: &Float,
107    bits: T,
108    rm: RoundingMode,
109) -> (Float, Ordering)
110where
111    i32: WrappingFrom<T>,
112{
113    if let Float(Finite {
114        significand,
115        exponent,
116        sign,
117        precision,
118    }) = x
119    {
120        let mut possibly_just_under_min = false;
121        if let Ok(bits) = bits.try_into() {
122            if let Some(new_exponent) = exponent.checked_sub(bits) {
123                possibly_just_under_min = true;
124                if (Float::MIN_EXPONENT..=Float::MAX_EXPONENT).contains(&new_exponent) {
125                    return (
126                        Float(Finite {
127                            significand: significand.clone(),
128                            exponent: new_exponent,
129                            sign: *sign,
130                            precision: *precision,
131                        }),
132                        Equal,
133                    );
134                }
135            }
136        }
137        assert!(rm != Exact, "Inexact Float right-shift");
138        if bits < T::ZERO {
139            match (*sign, rm) {
140                (true, Up | Ceiling | Nearest) => (Float::INFINITY, Greater),
141                (true, Floor | Down) => (Float::max_finite_value_with_prec(*precision), Less),
142                (false, Up | Floor | Nearest) => (Float::NEGATIVE_INFINITY, Less),
143                (false, Ceiling | Down) => {
144                    (-Float::max_finite_value_with_prec(*precision), Greater)
145                }
146                (_, Exact) => unreachable!(),
147            }
148        } else if rm == Nearest
149            && possibly_just_under_min
150            && *exponent - i32::wrapping_from(bits) == Float::MIN_EXPONENT - 1
151            && !significand.is_power_of_2()
152        {
153            if *sign {
154                (Float::min_positive_value_prec(*precision), Greater)
155            } else {
156                (-Float::min_positive_value_prec(*precision), Less)
157            }
158        } else {
159            match (*sign, rm) {
160                (true, Up | Ceiling) => (Float::min_positive_value_prec(*precision), Greater),
161                (true, Floor | Down | Nearest) => (Float::ZERO, Less),
162                (false, Up | Floor) => (-Float::min_positive_value_prec(*precision), Less),
163                (false, Ceiling | Down | Nearest) => (Float::NEGATIVE_ZERO, Greater),
164                (_, Exact) => unreachable!(),
165            }
166        }
167    } else {
168        (x.clone(), Equal)
169    }
170}
171
172fn shr_round_assign_primitive_int<T: PrimitiveInt>(
173    x: &mut Float,
174    bits: T,
175    rm: RoundingMode,
176) -> Ordering
177where
178    i32: WrappingFrom<T>,
179{
180    shr_prec_round_assign_helper(x, bits, x.significant_bits(), rm, Equal)
181}
182
183macro_rules! impl_natural_shr_round {
184    ($t:ident) => {
185        impl ShrRound<$t> for Float {
186            type Output = Float;
187
188            /// Right-shifts a [`Float`] (divides it by a power of 2), taking the [`Float`] by
189            /// value.
190            ///
191            /// `NaN`, infinities, and zeros are unchanged. If the [`Float`] has a precision, the
192            /// output has the same precision.
193            ///
194            /// $$
195            /// f(x,k,m) = x/2^k.
196            /// $$
197            ///
198            /// - If $f(x,k,m)\geq 2^{2^{30}-1}$ and $m$ is `Ceiling`, `Up`, or `Nearest`, $\infty$
199            ///   is returned instead.
200            /// - If $f(x,k,m)\geq 2^{2^{30}-1}$ and $m$ is `Floor` or `Down`,
201            ///   $(1-(1/2)^p)2^{2^{30}-1}$ is returned instead, where `p` is the precision of the
202            ///   input.
203            /// - If $f(x,k,m)\geq 2^{2^{30}-1}$ and $m$ is `Floor`, `Up`, or `Nearest`, $-\infty$
204            ///   is returned instead.
205            /// - If $f(x,k,m)\geq 2^{2^{30}-1}$ and $m$ is `Ceiling` or `Down`,
206            ///   $-(1-(1/2)^p)2^{2^{30}-1}$ is returned instead, where `p` is the precision of the
207            ///   input.
208            /// - If $0<f(x,k,m)<2^{-2^{30}}$, and $m$ is `Floor` or `Down`, $0.0$ is returned
209            ///   instead.
210            /// - If $0<f(x,k,m)<2^{-2^{30}}$, and $m$ is `Ceiling` or `Up`, $2^{-2^{30}}$ is
211            ///   returned instead.
212            /// - If $0<f(x,k,m)\leq2^{-2^{30}-1}$, and $m$ is `Nearest`, $0.0$ is returned instead.
213            /// - If $2^{-2^{30}-1}<f(x,k,m)<2^{-2^{30}}$, and $m$ is `Nearest`, $2^{-2^{30}}$ is
214            ///   returned instead.
215            /// - If $-2^{-2^{30}}<f(x,k,m)<0$, and $m$ is `Ceiling` or `Down`, $-0.0$ is returned
216            ///   instead.
217            /// - If $-2^{-2^{30}}<f(x,k,m)<0$, and $m$ is `Floor` or `Up`, $-2^{-2^{30}}$ is
218            ///   returned instead.
219            /// - If $-2^{-2^{30}-1}\leq f(x,k,m)<0$, and $m$ is `Nearest`, $-0.0$ is returned
220            ///   instead.
221            /// - If $-2^{-2^{30}}<f(x,k,m)<-2^{-2^{30}-1}$, and $m$ is `Nearest`, $-2^{-2^{30}}$ is
222            ///   returned instead.
223            ///
224            /// If you don't care about overflow or underflow behavior, or only want the behavior of
225            /// the `Nearest` rounding mode, you can just use `>>` instead.
226            ///
227            /// # Worst-case complexity
228            /// $T(n) = O(n)$
229            ///
230            /// $M(n) = O(n)$
231            ///
232            /// where $T$ is time, $M$ is additional memory, and $n$ is `self.significant_bits()`.
233            ///
234            /// # Panics
235            /// Panics if the result overflows or underflows and `rm` is `Exact`.
236            ///
237            /// # Examples
238            /// See [here](super::shr_round#shr_round).
239            #[inline]
240            fn shr_round(mut self, bits: $t, rm: RoundingMode) -> (Float, Ordering) {
241                let o = self.shr_round_assign(bits, rm);
242                (self, o)
243            }
244        }
245
246        impl ShrRound<$t> for &Float {
247            type Output = Float;
248
249            /// Right-shifts a [`Float`] (divides it by a power of 2), taking the [`Float`] by
250            /// reference.
251            ///
252            /// `NaN`, infinities, and zeros are unchanged. If the [`Float`] has a precision, the
253            /// output has the same precision.
254            ///
255            /// $$
256            /// f(x,k,m) = x/2^k.
257            /// $$
258            ///
259            /// - If $f(x,k,m)\geq 2^{2^{30}-1}$ and $m$ is `Ceiling`, `Up`, or `Nearest`, $\infty$
260            ///   is returned instead.
261            /// - If $f(x,k,m)\geq 2^{2^{30}-1}$ and $m$ is `Floor` or `Down`,
262            ///   $(1-(1/2)^p)2^{2^{30}-1}$ is returned instead, where `p` is the precision of the
263            ///   input.
264            /// - If $f(x,k,m)\geq 2^{2^{30}-1}$ and $m$ is `Floor`, `Up`, or `Nearest`, $-\infty$
265            ///   is returned instead.
266            /// - If $f(x,k,m)\geq 2^{2^{30}-1}$ and $m$ is `Ceiling` or `Down`,
267            ///   $-(1-(1/2)^p)2^{2^{30}-1}$ is returned instead, where `p` is the precision of the
268            ///   input.
269            /// - If $0<f(x,k,m)<2^{-2^{30}}$, and $m$ is `Floor` or `Down`, $0.0$ is returned
270            ///   instead.
271            /// - If $0<f(x,k,m)<2^{-2^{30}}$, and $m$ is `Ceiling` or `Up`, $2^{-2^{30}}$ is
272            ///   returned instead.
273            /// - If $0<f(x,k,m)\leq2^{-2^{30}-1}$, and $m$ is `Nearest`, $0.0$ is returned instead.
274            /// - If $2^{-2^{30}-1}<f(x,k,m)<2^{-2^{30}}$, and $m$ is `Nearest`, $2^{-2^{30}}$ is
275            ///   returned instead.
276            /// - If $-2^{-2^{30}}<f(x,k,m)<0$, and $m$ is `Ceiling` or `Down`, $-0.0$ is returned
277            ///   instead.
278            /// - If $-2^{-2^{30}}<f(x,k,m)<0$, and $m$ is `Floor` or `Up`, $-2^{-2^{30}}$ is
279            ///   returned instead.
280            /// - If $-2^{-2^{30}-1}\leq f(x,k,m)<0$, and $m$ is `Nearest`, $-0.0$ is returned
281            ///   instead.
282            /// - If $-2^{-2^{30}}<f(x,k,m)<-2^{-2^{30}-1}$, and $m$ is `Nearest`, $-2^{-2^{30}}$ is
283            ///   returned instead.
284            ///
285            /// If you don't care about overflow or underflow behavior, or only want the behavior of
286            /// the `Nearest` rounding mode, you can just use `>>` instead.
287            ///
288            /// # Worst-case complexity
289            /// $T(n) = O(n)$
290            ///
291            /// $M(n) = O(n)$
292            ///
293            /// where $T$ is time, $M$ is additional memory, and $n$ is `self.significant_bits()`.
294            ///
295            /// # Panics
296            /// Panics if the result overflows or underflows and `rm` is `Exact`.
297            ///
298            /// # Examples
299            /// See [here](super::shr_round#shr_round).
300            #[inline]
301            fn shr_round(self, bits: $t, rm: RoundingMode) -> (Float, Ordering) {
302                shr_round_primitive_int_ref(self, bits, rm)
303            }
304        }
305
306        impl ShrRoundAssign<$t> for Float {
307            /// Right-shifts a [`Float`] (divides it by a power of 2), in place.
308            ///
309            /// `NaN`, infinities, and zeros are unchanged. If the [`Float`] has a precision, the
310            /// precision is unchanged.
311            ///
312            /// $$
313            /// x\gets x/2^k.
314            /// $$
315            ///
316            /// - If $f(x,k,m)\geq 2^{2^{30}-1}$ and $m$ is `Ceiling`, `Up`, or `Nearest`, $\infty$
317            ///   is returned instead.
318            /// - If $f(x,k,m)\geq 2^{2^{30}-1}$ and $m$ is `Floor` or `Down`,
319            ///   $(1-(1/2)^p)2^{2^{30}-1}$ is returned instead, where `p` is the precision of the
320            ///   input.
321            /// - If $f(x,k,m)\geq 2^{2^{30}-1}$ and $m$ is `Floor`, `Up`, or `Nearest`, $-\infty$
322            ///   is returned instead.
323            /// - If $f(x,k,m)\geq 2^{2^{30}-1}$ and $m$ is `Ceiling` or `Down`,
324            ///   $-(1-(1/2)^p)2^{2^{30}-1}$ is returned instead, where `p` is the precision of the
325            ///   input.
326            /// - If $0<f(x,k,m)<2^{-2^{30}}$, and $m$ is `Floor` or `Down`, $0.0$ is returned
327            ///   instead.
328            /// - If $0<f(x,k,m)<2^{-2^{30}}$, and $m$ is `Ceiling` or `Up`, $2^{-2^{30}}$ is
329            ///   returned instead.
330            /// - If $0<f(x,k,m)\leq2^{-2^{30}-1}$, and $m$ is `Nearest`, $0.0$ is returned instead.
331            /// - If $2^{-2^{30}-1}<f(x,k,m)<2^{-2^{30}}$, and $m$ is `Nearest`, $2^{-2^{30}}$ is
332            ///   returned instead.
333            /// - If $-2^{-2^{30}}<f(x,k,m)<0$, and $m$ is `Ceiling` or `Down`, $-0.0$ is returned
334            ///   instead.
335            /// - If $-2^{-2^{30}}<f(x,k,m)<0$, and $m$ is `Floor` or `Up`, $-2^{-2^{30}}$ is
336            ///   returned instead.
337            /// - If $-2^{-2^{30}-1}\leq f(x,k,m)<0$, and $m$ is `Nearest`, $-0.0$ is returned
338            ///   instead.
339            /// - If $-2^{-2^{30}}<f(x,k,m)<-2^{-2^{30}-1}$, and $m$ is `Nearest`, $-2^{-2^{30}}$ is
340            ///   returned instead.
341            ///
342            /// If you don't care about overflow or underflow behavior, or only want the behavior of
343            /// the `Nearest` rounding mode, you can just use `>>=` instead.
344            ///
345            /// # Worst-case complexity
346            /// $T(n) = O(n)$
347            ///
348            /// $M(n) = O(n)$
349            ///
350            /// where $T$ is time, $M$ is additional memory, and $n$ is `self.significant_bits()`.
351            ///
352            /// # Panics
353            /// Panics if the result overflows or underflows and `rm` is `Exact`.
354            ///
355            /// # Examples
356            /// See [here](super::shr_round#shr_round_assign).
357            #[inline]
358            fn shr_round_assign(&mut self, bits: $t, rm: RoundingMode) -> Ordering {
359                shr_round_assign_primitive_int(self, bits, rm)
360            }
361        }
362    };
363}
364apply_to_primitive_ints!(impl_natural_shr_round);