malachite_q/conversion/mantissa_and_exponent.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::Rational;
10use core::cmp::Ordering::{self, *};
11use malachite_base::num::arithmetic::traits::DivRound;
12use malachite_base::num::basic::floats::PrimitiveFloat;
13use malachite_base::num::conversion::traits::{
14 ExactFrom, IntegerMantissaAndExponent, SciMantissaAndExponent, WrappingFrom,
15};
16use malachite_base::num::logic::traits::{BitAccess, SignificantBits};
17use malachite_base::rounding_modes::RoundingMode::{self, *};
18
19impl Rational {
20 /// Returns a [`Rational`]'s scientific mantissa and exponent, taking the [`Rational`] by value.
21 /// An [`Ordering`] is also returned, indicating whether the returned mantissa and exponent
22 /// represent a value that is less than, equal to, or greater than the absolute value of the
23 /// [`Rational`].
24 ///
25 /// The [`Rational`]'s sign is ignored. This means that, for example, that rounding using
26 /// `Floor` is equivalent to rounding using `Down`, even if the [`Rational`] is negative.
27 ///
28 /// When $x$ is positive, we can write $x = 2^{e_s}m_s$, where $e_s$ is an integer and $m_s$ is
29 /// a rational number with $1 \leq m_s < 2$. We represent the rational mantissa as a float. The
30 /// conversion might not be exact, so we round to the nearest float using the provided rounding
31 /// mode. If the rounding mode is `Exact` but the conversion is not exact, `None` is returned.
32 /// $$
33 /// f(x, r) \approx \left (\frac{x}{2^{\lfloor \log_2 x \rfloor}},
34 /// \lfloor \log_2 x \rfloor\right ).
35 /// $$
36 ///
37 /// # Worst-case complexity
38 /// $T(n) = O(n \log n \log\log n)$
39 ///
40 /// $M(n) = O(n \log n)$
41 ///
42 /// where $T$ is time, $M$ is additional memory, and $n$ is `self.significant_bits()`.
43 ///
44 /// # Examples
45 /// ```
46 /// use malachite_base::num::float::NiceFloat;
47 /// use malachite_base::rounding_modes::RoundingMode::{self, *};
48 /// use malachite_q::Rational;
49 /// use std::cmp::Ordering::{self, *};
50 ///
51 /// let test = |n: Rational, rm: RoundingMode, out: Option<(f32, i64, Ordering)>| {
52 /// assert_eq!(
53 /// n.sci_mantissa_and_exponent_round(rm)
54 /// .map(|(m, e, o)| (NiceFloat(m), e, o)),
55 /// out.map(|(m, e, o)| (NiceFloat(m), e, o))
56 /// );
57 /// };
58 /// test(Rational::from(3u32), Down, Some((1.5, 1, Equal)));
59 /// test(Rational::from(3u32), Ceiling, Some((1.5, 1, Equal)));
60 /// test(Rational::from(3u32), Up, Some((1.5, 1, Equal)));
61 /// test(Rational::from(3u32), Nearest, Some((1.5, 1, Equal)));
62 /// test(Rational::from(3u32), Exact, Some((1.5, 1, Equal)));
63 ///
64 /// test(
65 /// Rational::from_signeds(1, 3),
66 /// Floor,
67 /// Some((1.3333333, -2, Less)),
68 /// );
69 /// test(
70 /// Rational::from_signeds(1, 3),
71 /// Down,
72 /// Some((1.3333333, -2, Less)),
73 /// );
74 /// test(
75 /// Rational::from_signeds(1, 3),
76 /// Ceiling,
77 /// Some((1.3333334, -2, Greater)),
78 /// );
79 /// test(
80 /// Rational::from_signeds(1, 3),
81 /// Up,
82 /// Some((1.3333334, -2, Greater)),
83 /// );
84 /// test(
85 /// Rational::from_signeds(1, 3),
86 /// Nearest,
87 /// Some((1.3333334, -2, Greater)),
88 /// );
89 /// test(Rational::from_signeds(1, 3), Exact, None);
90 ///
91 /// test(
92 /// Rational::from_signeds(-1, 3),
93 /// Floor,
94 /// Some((1.3333333, -2, Less)),
95 /// );
96 /// test(
97 /// Rational::from_signeds(-1, 3),
98 /// Down,
99 /// Some((1.3333333, -2, Less)),
100 /// );
101 /// test(
102 /// Rational::from_signeds(-1, 3),
103 /// Ceiling,
104 /// Some((1.3333334, -2, Greater)),
105 /// );
106 /// test(
107 /// Rational::from_signeds(-1, 3),
108 /// Up,
109 /// Some((1.3333334, -2, Greater)),
110 /// );
111 /// test(
112 /// Rational::from_signeds(-1, 3),
113 /// Nearest,
114 /// Some((1.3333334, -2, Greater)),
115 /// );
116 /// test(Rational::from_signeds(-1, 3), Exact, None);
117 /// ```
118 pub fn sci_mantissa_and_exponent_round<T: PrimitiveFloat>(
119 mut self,
120 rm: RoundingMode,
121 ) -> Option<(T, i64, Ordering)> {
122 assert!(self != 0);
123 let mut exponent = i64::exact_from(self.numerator.significant_bits())
124 - i64::exact_from(self.denominator.significant_bits());
125 if self.numerator.cmp_normalized(&self.denominator) == Less {
126 exponent -= 1;
127 }
128 self >>= exponent - i64::wrapping_from(T::MANTISSA_WIDTH);
129 let (n, d) = self.into_numerator_and_denominator();
130 if rm == Exact && d != 1u32 {
131 return None;
132 }
133 let (mut mantissa, o) = n.div_round(d, rm);
134 let mut bits = mantissa.significant_bits();
135 if bits > T::MANTISSA_WIDTH + 1 {
136 bits -= 1;
137 mantissa >>= 1;
138 exponent += 1;
139 }
140 assert_eq!(bits, T::MANTISSA_WIDTH + 1);
141 mantissa.clear_bit(T::MANTISSA_WIDTH);
142 Some((
143 T::from_raw_mantissa_and_exponent(
144 u64::exact_from(&mantissa),
145 u64::wrapping_from(T::MAX_EXPONENT),
146 ),
147 exponent,
148 o,
149 ))
150 }
151
152 /// Returns a [`Rational`]'s scientific mantissa and exponent, taking the [`Rational`] by
153 /// reference. An [`Ordering`] is also returned, indicating whether the returned mantissa and
154 /// exponent represent a value that is less than, equal to, or greater than the original value.
155 ///
156 /// When $x$ is positive, we can write $x = 2^{e_s}m_s$, where $e_s$ is an integer and $m_s$ is
157 /// a rational number with $1 \leq m_s < 2$. We represent the rational mantissa as a float. The
158 /// conversion might not be exact, so we round to the nearest float using the provided rounding
159 /// mode. If the rounding mode is `Exact` but the conversion is not exact, `None` is returned.
160 /// $$
161 /// f(x, r) \approx \left (\frac{x}{2^{\lfloor \log_2 x \rfloor}},
162 /// \lfloor \log_2 x \rfloor\right ).
163 /// $$
164 ///
165 /// # Worst-case complexity
166 /// $T(n) = O(n \log n \log\log n)$
167 ///
168 /// $M(n) = O(n \log n)$
169 ///
170 /// where $T$ is time, $M$ is additional memory, and $n$ is `self.significant_bits()`.
171 ///
172 /// # Examples
173 /// ```
174 /// use malachite_base::num::float::NiceFloat;
175 /// use malachite_base::rounding_modes::RoundingMode::{self, *};
176 /// use malachite_q::Rational;
177 /// use std::cmp::Ordering::{self, *};
178 ///
179 /// let test = |n: Rational, rm: RoundingMode, out: Option<(f32, i64, Ordering)>| {
180 /// assert_eq!(
181 /// n.sci_mantissa_and_exponent_round_ref(rm)
182 /// .map(|(m, e, o)| (NiceFloat(m), e, o)),
183 /// out.map(|(m, e, o)| (NiceFloat(m), e, o))
184 /// );
185 /// };
186 /// test(Rational::from(3u32), Down, Some((1.5, 1, Equal)));
187 /// test(Rational::from(3u32), Ceiling, Some((1.5, 1, Equal)));
188 /// test(Rational::from(3u32), Up, Some((1.5, 1, Equal)));
189 /// test(Rational::from(3u32), Nearest, Some((1.5, 1, Equal)));
190 /// test(Rational::from(3u32), Exact, Some((1.5, 1, Equal)));
191 ///
192 /// test(
193 /// Rational::from_signeds(1, 3),
194 /// Floor,
195 /// Some((1.3333333, -2, Less)),
196 /// );
197 /// test(
198 /// Rational::from_signeds(1, 3),
199 /// Down,
200 /// Some((1.3333333, -2, Less)),
201 /// );
202 /// test(
203 /// Rational::from_signeds(1, 3),
204 /// Ceiling,
205 /// Some((1.3333334, -2, Greater)),
206 /// );
207 /// test(
208 /// Rational::from_signeds(1, 3),
209 /// Up,
210 /// Some((1.3333334, -2, Greater)),
211 /// );
212 /// test(
213 /// Rational::from_signeds(1, 3),
214 /// Nearest,
215 /// Some((1.3333334, -2, Greater)),
216 /// );
217 /// test(Rational::from_signeds(1, 3), Exact, None);
218 /// ```
219 pub fn sci_mantissa_and_exponent_round_ref<T: PrimitiveFloat>(
220 &self,
221 rm: RoundingMode,
222 ) -> Option<(T, i64, Ordering)> {
223 assert!(*self != 0);
224 let mut exponent = i64::exact_from(self.numerator.significant_bits())
225 - i64::exact_from(self.denominator.significant_bits());
226 if self.numerator.cmp_normalized(&self.denominator) == Less {
227 exponent -= 1;
228 }
229 let x = self >> (exponent - i64::wrapping_from(T::MANTISSA_WIDTH));
230 let (n, d) = x.into_numerator_and_denominator();
231 if rm == Exact && d != 1u32 {
232 return None;
233 }
234 let (mut mantissa, o) = n.div_round(d, rm);
235 let mut bits = mantissa.significant_bits();
236 if bits > T::MANTISSA_WIDTH + 1 {
237 bits -= 1;
238 mantissa >>= 1;
239 exponent += 1;
240 }
241 assert_eq!(bits, T::MANTISSA_WIDTH + 1);
242 mantissa.clear_bit(T::MANTISSA_WIDTH);
243 Some((
244 T::from_raw_mantissa_and_exponent(
245 u64::exact_from(&mantissa),
246 u64::wrapping_from(T::MAX_EXPONENT),
247 ),
248 exponent,
249 o,
250 ))
251 }
252}
253
254macro_rules! impl_mantissa_and_exponent {
255 ($t:ident) => {
256 impl SciMantissaAndExponent<$t, i64> for Rational {
257 /// Returns a [`Rational`]'s scientific mantissa and exponent, taking the [`Rational`]
258 /// by value.
259 ///
260 /// When $x$ is positive, we can write $x = 2^{e_s}m_s$, where $e_s$ is an integer and
261 /// $m_s$ is a rational number with $1 \leq m_s < 2$. We represent the rational mantissa
262 /// as a float. The conversion might not be exact, so we round to the nearest float
263 /// using the `Nearest` rounding mode. To use other rounding modes, use
264 /// [`sci_mantissa_and_exponent_round`](Rational::sci_mantissa_and_exponent_round).
265 /// $$
266 /// f(x) \approx (\frac{x}{2^{\lfloor \log_2 x \rfloor}}, \lfloor \log_2 x \rfloor).
267 /// $$
268 ///
269 /// # Worst-case complexity
270 /// $T(n) = O(n \log n \log\log n)$
271 ///
272 /// $M(n) = O(n \log n)$
273 ///
274 /// where $T$ is time, $M$ is additional memory, and $n$ is `self.significant_bits()`.
275 ///
276 /// # Examples
277 /// See [here](super::mantissa_and_exponent#sci_mantissa_and_exponent).
278 #[inline]
279 fn sci_mantissa_and_exponent(self) -> ($t, i64) {
280 let (m, e, _) = self.sci_mantissa_and_exponent_round(Nearest).unwrap();
281 (m, e)
282 }
283
284 /// Returns a [`Rational`]'s scientific exponent, taking the [`Rational`] by value.
285 ///
286 /// When $x$ is positive, we can write $x = 2^{e_s}m_s$, where $e_s$ is an integer and
287 /// $m_s$ is a rational number with $1 \leq m_s < 2$. We represent the rational mantissa
288 /// as a float. The conversion might not be exact, so we round to the nearest float
289 /// using the `Nearest` rounding mode. To use other rounding modes, use
290 /// [`sci_mantissa_and_exponent_round`](Rational::sci_mantissa_and_exponent_round).
291 /// $$
292 /// f(x) \approx \lfloor \log_2 x \rfloor.
293 /// $$
294 ///
295 /// # Worst-case complexity
296 /// $T(n) = O(n \log n \log\log n)$
297 ///
298 /// $M(n) = O(n \log n)$
299 ///
300 /// where $T$ is time, $M$ is additional memory, and $n$ is `self.significant_bits()`.
301 ///
302 /// # Examples
303 /// See [here](super::mantissa_and_exponent#sci_exponent).
304 fn sci_exponent(mut self) -> i64 {
305 assert!(self != 0);
306 let mut exponent = i64::exact_from(self.numerator.significant_bits())
307 - i64::exact_from(self.denominator.significant_bits());
308 if self.numerator.cmp_normalized(&self.denominator) == Less {
309 exponent -= 1;
310 }
311 self >>= exponent - i64::wrapping_from($t::MANTISSA_WIDTH);
312 let (n, d) = self.into_numerator_and_denominator();
313 if n.div_round(d, Nearest).0.significant_bits() > $t::MANTISSA_WIDTH + 1 {
314 exponent + 1
315 } else {
316 exponent
317 }
318 }
319
320 /// Constructs a [`Rational`] from its scientific mantissa and exponent.
321 ///
322 /// When $x$ is positive, we can write $x = 2^{e_s}m_s$, where $e_s$ is an integer and
323 /// $m_s$ is a rational number with $1 \leq m_s < 2$. Here, the rational mantissa is
324 /// provided as a float. If the mantissa is outside the range $[1, 2)$, `None` is
325 /// returned.
326 ///
327 /// All finite floats can be represented using [`Rational`]s, so no rounding is needed.
328 ///
329 /// $$
330 /// f(x) \approx 2^{e_s}m_s.
331 /// $$
332 ///
333 /// # Worst-case complexity
334 /// $T(n) = O(n)$
335 ///
336 /// $M(n) = O(n)$
337 ///
338 /// where $T$ is time, $M$ is additional memory, and $n$ is `sci_exponent`.
339 #[allow(clippy::manual_range_contains)]
340 #[inline]
341 fn from_sci_mantissa_and_exponent(
342 sci_mantissa: $t,
343 sci_exponent: i64,
344 ) -> Option<Rational> {
345 assert_ne!(sci_mantissa, 0.0);
346 if sci_mantissa < 1.0 || sci_mantissa >= 2.0 {
347 None
348 } else {
349 let m = sci_mantissa.integer_mantissa();
350 Some(
351 Rational::from(m)
352 << (sci_exponent - i64::exact_from(m.significant_bits()) + 1),
353 )
354 }
355 }
356 }
357
358 impl SciMantissaAndExponent<$t, i64, Rational> for &Rational {
359 /// Returns a [`Rational`]'s scientific mantissa and exponent, taking the [`Rational`]
360 /// by reference.
361 ///
362 /// When $x$ is positive, we can write $x = 2^{e_s}m_s$, where $e_s$ is an integer and
363 /// $m_s$ is a rational number with $1 \leq m_s < 2$. We represent the rational mantissa
364 /// as a float. The conversion might not be exact, so we round to the nearest float
365 /// using the `Nearest` rounding mode. To use other rounding modes, use
366 /// [`sci_mantissa_and_exponent_round`](Rational::sci_mantissa_and_exponent_round).
367 /// $$
368 /// f(x) \approx (\frac{x}{2^{\lfloor \log_2 x \rfloor}}, \lfloor \log_2 x \rfloor).
369 /// $$
370 ///
371 /// # Worst-case complexity
372 /// $T(n) = O(n \log n \log\log n)$
373 ///
374 /// $M(n) = O(n \log n)$
375 ///
376 /// where $T$ is time, $M$ is additional memory, and $n$ is `self.significant_bits()`.
377 ///
378 /// # Examples
379 /// See [here](super::mantissa_and_exponent#sci_mantissa_and_exponent).
380 #[inline]
381 fn sci_mantissa_and_exponent(self) -> ($t, i64) {
382 let (m, e, _) = self.sci_mantissa_and_exponent_round_ref(Nearest).unwrap();
383 (m, e)
384 }
385
386 /// Returns a [`Rational`]'s scientific exponent, taking the [`Rational`] by reference.
387 ///
388 /// When $x$ is positive, we can write $x = 2^{e_s}m_s$, where $e_s$ is an integer and
389 /// $m_s$ is a rational number with $1 \leq m_s < 2$. We represent the rational mantissa
390 /// as a float. The conversion might not be exact, so we round to the nearest float
391 /// using the `Nearest` rounding mode. To use other rounding modes, use
392 /// [`sci_mantissa_and_exponent_round`](Rational::sci_mantissa_and_exponent_round).
393 /// $$
394 /// f(x) \approx \lfloor \log_2 x \rfloor.
395 /// $$
396 ///
397 /// # Worst-case complexity
398 /// $T(n) = O(n \log n \log\log n)$
399 ///
400 /// $M(n) = O(n \log n)$
401 ///
402 /// where $T$ is time, $M$ is additional memory, and $n$ is `self.significant_bits()`.
403 fn sci_exponent(self) -> i64 {
404 assert!(*self != 0);
405 let mut exponent = i64::exact_from(self.numerator.significant_bits())
406 - i64::exact_from(self.denominator.significant_bits());
407 if self.numerator.cmp_normalized(&self.denominator) == Less {
408 exponent -= 1;
409 }
410 let x = self >> exponent - i64::wrapping_from($t::MANTISSA_WIDTH);
411 let (n, d) = x.into_numerator_and_denominator();
412 if n.div_round(d, Nearest).0.significant_bits() > $t::MANTISSA_WIDTH + 1 {
413 exponent + 1
414 } else {
415 exponent
416 }
417 }
418
419 /// Constructs a [`Rational`] from its scientific mantissa and exponent.
420 ///
421 /// When $x$ is positive, we can write $x = 2^{e_s}m_s$, where $e_s$ is an integer and
422 /// $m_s$ is a rational number with $1 \leq m_s < 2$. Here, the rational mantissa is
423 /// provided as a float. If the mantissa is outside the range $[1, 2)$, `None` is
424 /// returned.
425 ///
426 /// All finite floats can be represented using [`Rational`]s, so no rounding is needed.
427 ///
428 /// $$
429 /// f(x) \approx 2^{e_s}m_s.
430 /// $$
431 ///
432 /// # Worst-case complexity
433 /// $T(n) = O(n)$
434 ///
435 /// $M(n) = O(n)$
436 ///
437 /// where $T$ is time, $M$ is additional memory, and $n$ is `sci_exponent`.
438 ///
439 /// See [here](super::mantissa_and_exponent#from_sci_mantissa_and_exponent).
440 #[inline]
441 fn from_sci_mantissa_and_exponent(
442 sci_mantissa: $t,
443 sci_exponent: i64,
444 ) -> Option<Rational> {
445 Rational::from_sci_mantissa_and_exponent(sci_mantissa, sci_exponent)
446 }
447 }
448 };
449}
450apply_to_primitive_floats!(impl_mantissa_and_exponent);