malachite_float/arithmetic/shl_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 crate::malachite_base::num::logic::traits::SignificantBits;
12use core::cmp::Ordering::{self, *};
13use malachite_base::num::arithmetic::traits::{IsPowerOf2, ShlRound, ShlRoundAssign};
14use malachite_base::num::basic::integers::PrimitiveInt;
15use malachite_base::num::basic::traits::{Infinity, NegativeInfinity, NegativeZero, Zero};
16use malachite_base::num::conversion::traits::WrappingFrom;
17use malachite_base::rounding_modes::RoundingMode::{self, *};
18
19pub(crate) fn shl_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_add(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 left-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 shl_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_add(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 left-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 shl_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 shl_prec_round_assign_helper(x, bits, x.significant_bits(), rm, Equal)
181}
182
183macro_rules! impl_natural_shl_round {
184 ($t:ident) => {
185 impl ShlRound<$t> for Float {
186 type Output = Float;
187
188 /// Left-shifts a [`Float`] (multiplies 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) = x2^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::shl_round#shl_round).
239 #[inline]
240 fn shl_round(mut self, bits: $t, rm: RoundingMode) -> (Float, Ordering) {
241 let o = self.shl_round_assign(bits, rm);
242 (self, o)
243 }
244 }
245
246 impl ShlRound<$t> for &Float {
247 type Output = Float;
248
249 /// Left-shifts a [`Float`] (multiplies 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) = x2^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::shl_round#shl_round).
300 #[inline]
301 fn shl_round(self, bits: $t, rm: RoundingMode) -> (Float, Ordering) {
302 shl_round_primitive_int_ref(self, bits, rm)
303 }
304 }
305
306 impl ShlRoundAssign<$t> for Float {
307 /// Left-shifts a [`Float`] (multiplies 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 x2^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::shl_round#shl_round_assign).
357 #[inline]
358 fn shl_round_assign(&mut self, bits: $t, rm: RoundingMode) -> Ordering {
359 shl_round_assign_primitive_int(self, bits, rm)
360 }
361 }
362 };
363}
364apply_to_primitive_ints!(impl_natural_shl_round);