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);