Skip to main content

malachite_q/conversion/
primitive_float_from_rational.rs

1// Copyright © 2026 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::{
12    DivRound, DivisibleByPowerOf2, IsPowerOf2, NegAssign,
13};
14use malachite_base::num::basic::floats::PrimitiveFloat;
15use malachite_base::num::conversion::traits::{
16    ConvertibleFrom, ExactFrom, RawMantissaAndExponent, RoundingFrom, SciMantissaAndExponent,
17    WrappingFrom,
18};
19use malachite_base::num::logic::traits::{BitAccess, SignificantBits};
20use malachite_base::rounding_modes::RoundingMode::{self, *};
21
22#[derive(Clone, Copy, Debug, Eq, PartialEq)]
23pub enum FloatConversionError {
24    Inexact,
25    Overflow,
26    Underflow,
27}
28
29fn abs_is_neg_power_of_2(x: &Rational) -> bool {
30    x.numerator == 1u32 && x.denominator.is_power_of_2()
31}
32
33macro_rules! float_impls {
34    ($f: ident) => {
35        impl RoundingFrom<Rational> for $f {
36            /// Converts a [`Rational`] to a value of a primitive float according to a specified
37            /// [`RoundingMode`], taking the [`Rational`] by value.
38            ///
39            /// - If the rounding mode is `Floor`, the largest float less than or equal to the
40            ///   [`Rational`] is returned. If the [`Rational`] is greater than the maximum finite
41            ///   float, then the maximum finite float is returned. If it is smaller than the
42            ///   minimum finite float, then $-\infty$ is returned. If it is between zero and the
43            ///   minimum positive float, then positive zero is returned.
44            /// - If the rounding mode is `Ceiling`, the smallest float greater than or equal to the
45            ///   [`Rational`] is returned. If the [`Rational`] is greater than the maximum finite
46            ///   float, then $\infty$ is returned. If it is smaller than the minimum finite float,
47            ///   then the minimum finite float is returned. If it is between zero and the maximum
48            ///   negative float, then negative zero is returned.
49            /// - If the rounding mode is `Down`, then the rounding proceeds as with `Floor` if the
50            ///   [`Rational`] is non-negative and as with `Ceiling` if the [`Rational`] is
51            ///   negative. If the [`Rational`] is between the maximum negative float and the
52            ///   minimum positive float, then positive zero is returned when the [`Rational`] is
53            ///   non-negative and negative zero otherwise.
54            /// - If the rounding mode is `Up`, then the rounding proceeds as with `Ceiling` if the
55            ///   [`Rational`] is non-negative and as with `Floor` if the [`Rational`] is negative.
56            ///   Positive zero is only returned when the [`Rational`] is zero, and negative zero is
57            ///   never returned.
58            /// - If the rounding mode is `Nearest`, then the nearest float is returned. If the
59            ///   [`Rational`] is exactly between two floats, the float with the zero
60            ///   least-significant bit in its representation is selected. If the [`Rational`] is
61            ///   greater than the maximum finite float, then $\infty$ is returned. If the
62            ///   [`Rational`] is smaller than the minimum finite float, then $-\infty$ is returned.
63            ///   If the [`Rational`] is closer to zero than to any other float (or if there is a
64            ///   tie between zero and another float), then positive or negative zero is returned,
65            ///   depending on the [`Rational`]'s sign.
66            ///
67            /// # Worst-case complexity
68            /// $T(n) = O(n \log n \log\log n)$
69            ///
70            /// $M(n) = O(n \log n)$
71            ///
72            /// where $T$ is time, $M$ is additional memory, and $n$ is `value.significant_bits()`.
73            ///
74            /// # Panics
75            /// Panics if the rounding mode is `Exact` and `value` cannot be represented exactly.
76            ///
77            /// # Examples
78            /// See [here](super::primitive_float_from_rational#rounding_from).
79            fn rounding_from(mut value: Rational, mut rm: RoundingMode) -> ($f, Ordering) {
80                if value == 0u32 {
81                    (0.0, Equal)
82                } else {
83                    let sign = value.sign;
84                    if !sign {
85                        rm.neg_assign();
86                    }
87                    let mut exponent = value.floor_log_base_2_abs();
88                    let (f, o) = if exponent > $f::MAX_EXPONENT {
89                        match rm {
90                            Exact => {
91                                panic!("Value cannot be represented exactly as a float")
92                            }
93                            Floor | Down => ($f::MAX_FINITE, Less),
94                            _ => ($f::INFINITY, Greater),
95                        }
96                    } else if exponent >= $f::MIN_NORMAL_EXPONENT {
97                        value >>= exponent - i64::wrapping_from($f::MANTISSA_WIDTH);
98                        let (n, d) = value.into_numerator_and_denominator();
99                        let (mut mantissa, o) = n.div_round(d, rm);
100                        let mut bits = mantissa.significant_bits();
101                        let mut done = false;
102                        if bits > $f::MANTISSA_WIDTH + 1 {
103                            if exponent == $f::MAX_EXPONENT {
104                                done = true;
105                            } else {
106                                bits -= 1;
107                                mantissa >>= 1; // lsb is zero
108                                exponent += 1;
109                            }
110                        }
111                        if done {
112                            match rm {
113                                Exact => {
114                                    panic!("Value cannot be represented exactly as a float")
115                                }
116                                Floor | Down => ($f::MAX_FINITE, Less),
117                                _ => ($f::INFINITY, Greater),
118                            }
119                        } else {
120                            assert_eq!(bits, $f::MANTISSA_WIDTH + 1);
121                            mantissa.clear_bit($f::MANTISSA_WIDTH);
122                            (
123                                $f::from_raw_mantissa_and_exponent(
124                                    u64::exact_from(&mantissa),
125                                    u64::exact_from(exponent + $f::MAX_EXPONENT),
126                                ),
127                                o,
128                            )
129                        }
130                    } else if exponent >= $f::MIN_EXPONENT {
131                        let target_width = u64::wrapping_from(exponent - $f::MIN_EXPONENT + 1);
132                        value >>= $f::MIN_EXPONENT;
133                        let (n, d) = value.into_numerator_and_denominator();
134                        let (mantissa, o) = n.div_round(d, rm);
135                        (
136                            if mantissa.significant_bits() > target_width
137                                && exponent == $f::MIN_NORMAL_EXPONENT - 1
138                            {
139                                $f::MIN_POSITIVE_NORMAL
140                            } else {
141                                $f::from_raw_mantissa_and_exponent(u64::exact_from(&mantissa), 0)
142                            },
143                            o,
144                        )
145                    } else {
146                        match rm {
147                            Exact => {
148                                panic!("Value cannot be represented exactly as a float")
149                            }
150                            Floor | Down => (0.0, Less),
151                            Nearest => {
152                                if exponent == $f::MIN_EXPONENT - 1
153                                    && !abs_is_neg_power_of_2(&value)
154                                {
155                                    ($f::MIN_POSITIVE_SUBNORMAL, Greater)
156                                } else {
157                                    (0.0, Less)
158                                }
159                            }
160                            _ => ($f::MIN_POSITIVE_SUBNORMAL, Greater),
161                        }
162                    };
163                    if sign { (f, o) } else { (-f, o.reverse()) }
164                }
165            }
166        }
167
168        impl TryFrom<Rational> for $f {
169            type Error = FloatConversionError;
170
171            /// Converts a [`Rational`] to a primitive float, taking the [`Rational`] by value. If
172            /// the input isn't exactly equal to any float, an error is returned.
173            ///
174            /// # Worst-case complexity
175            /// $T(n) = O(n)$
176            ///
177            /// $M(n) = O(1)$
178            ///
179            /// where $T$ is time, $M$ is additional memory, and $n$ is `value.significant_bits()`.
180            ///
181            /// # Examples
182            /// See [here](super::primitive_float_from_rational#try_from).
183            fn try_from(value: Rational) -> Result<$f, Self::Error> {
184                if value == 0 {
185                    Ok(0.0)
186                } else {
187                    let sign = value.sign;
188                    let (mantissa, exponent, _) = value
189                        .sci_mantissa_and_exponent_round(Exact)
190                        .ok_or(FloatConversionError::Inexact)?;
191                    let f = $f::from_sci_mantissa_and_exponent(mantissa, i64::exact_from(exponent))
192                        .ok_or(FloatConversionError::Inexact);
193                    if sign { f } else { f.map(|x| -x) }
194                }
195            }
196        }
197
198        impl ConvertibleFrom<Rational> for $f {
199            /// Determines whether a [`Rational`] can be exactly converted to a primitive float,
200            /// taking the [`Rational`] by value.
201            ///
202            /// # Worst-case complexity
203            /// $T(n) = O(n)$
204            ///
205            /// $M(n) = O(n)$
206            ///
207            /// where $T$ is time, $M$ is additional memory, and $n$ is `value.significant_bits()`.
208            ///
209            /// # Examples
210            /// See [here](super::primitive_float_from_rational#convertible_from).
211            fn convertible_from(value: Rational) -> bool {
212                if value == 0 {
213                    true
214                } else {
215                    if let Some((mantissa, exponent, _)) =
216                        value.sci_mantissa_and_exponent_round::<$f>(Exact)
217                    {
218                        let exponent = i64::exact_from(exponent);
219                        if !($f::MIN_EXPONENT..=$f::MAX_EXPONENT).contains(&exponent) {
220                            return false;
221                        }
222                        let (orig_mantissa, orig_exponent) = mantissa.raw_mantissa_and_exponent();
223                        orig_exponent == u64::wrapping_from($f::MAX_EXPONENT)
224                            && exponent >= $f::MIN_NORMAL_EXPONENT
225                            || orig_mantissa.divisible_by_power_of_2(u64::wrapping_from(
226                                $f::MIN_NORMAL_EXPONENT - exponent,
227                            ))
228                    } else {
229                        false
230                    }
231                }
232            }
233        }
234
235        impl RoundingFrom<&Rational> for $f {
236            /// Converts a [`Rational`] to a value of a primitive float according to a specified
237            /// [`RoundingMode`], taking the [`Rational`] by reference.
238            ///
239            /// - If the rounding mode is `Floor`, the largest float less than or equal to the
240            ///   [`Rational`] is returned. If the [`Rational`] is greater than the maximum finite
241            ///   float, then the maximum finite float is returned. If it is smaller than the
242            ///   minimum finite float, then $-\infty$ is returned. If it is between zero and the
243            ///   minimum positive float, then positive zero is returned.
244            /// - If the rounding mode is `Ceiling`, the smallest float greater than or equal to the
245            ///   [`Rational`] is returned. If the [`Rational`] is greater than the maximum finite
246            ///   float, then $\infty$is returned. If it is smaller than the minimum finite float,
247            ///   then the minimum finite float is returned. If it is between zero and the maximum
248            ///   negative float, then negative zero is returned.
249            /// - If the rounding mode is `Down`, then the rounding proceeds as with `Floor` if the
250            ///   [`Rational`] is non-negative and as with `Ceiling` if the [`Rational`] is
251            ///   negative. If the [`Rational`] is between the maximum negative float and the
252            ///   minimum positive float, then positive zero is returned when the [`Rational`] is
253            ///   non-negative and negative zero otherwise.
254            /// - If the rounding mode is `Up`, then the rounding proceeds as with `Ceiling` if the
255            ///   [`Rational`] is non-negative and as with `Floor` if the [`Rational`] is negative.
256            ///   Positive zero is only returned when the [`Rational`] is zero, and negative zero is
257            ///   never returned.
258            /// - If the rounding mode is `Nearest`, then the nearest float is returned. If the
259            ///   [`Rational`] is exactly between two floats, the float with the zero
260            ///   least-significant bit in its representation is selected. If the [`Rational`] is
261            ///   greater than the maximum finite float, then $\infty$ is returned. If the
262            ///   [`Rational`] is smaller than the minimum finite float, then $-\infty$ is returned.
263            ///   If the [`Rational`] is closer to zero than to any float (or if there is a tie
264            ///   between zero and another float), then positive or negative zero is returned,
265            ///   depending on the [`Rational`]'s sign.
266            ///
267            /// # Worst-case complexity
268            /// $T(n) = O(n \log n \log\log n)$
269            ///
270            /// $M(n) = O(n \log n)$
271            ///
272            /// where $T$ is time, $M$ is additional memory, and $n$ is `value.significant_bits()`.
273            ///
274            /// # Panics
275            /// Panics if the rounding mode is `Exact` and `value` cannot be represented exactly.
276            ///
277            /// # Examples
278            /// See [here](super::primitive_float_from_rational#rounding_from).
279            fn rounding_from(value: &Rational, mut rm: RoundingMode) -> ($f, Ordering) {
280                if *value == 0u32 {
281                    (0.0, Equal)
282                } else {
283                    if !value.sign {
284                        rm.neg_assign();
285                    }
286                    let mut exponent = value.floor_log_base_2_abs();
287                    let (f, o) = if exponent > $f::MAX_EXPONENT {
288                        match rm {
289                            Exact => {
290                                panic!("Value cannot be represented exactly as a float")
291                            }
292                            Floor | Down => ($f::MAX_FINITE, Less),
293                            _ => ($f::INFINITY, Greater),
294                        }
295                    } else if exponent >= $f::MIN_NORMAL_EXPONENT {
296                        let x = value >> exponent - i64::wrapping_from($f::MANTISSA_WIDTH);
297                        let (n, d) = x.into_numerator_and_denominator();
298                        let (mut mantissa, o) = n.div_round(d, rm);
299                        let mut bits = mantissa.significant_bits();
300                        let mut done = false;
301                        if bits > $f::MANTISSA_WIDTH + 1 {
302                            if exponent == $f::MAX_EXPONENT {
303                                done = true;
304                            } else {
305                                bits -= 1;
306                                mantissa >>= 1; // lsb is zero
307                                exponent += 1;
308                            }
309                        }
310                        if done {
311                            match rm {
312                                Exact => {
313                                    panic!("Value cannot be represented exactly as a float")
314                                }
315                                Floor | Down => ($f::MAX_FINITE, Less),
316                                _ => ($f::INFINITY, Greater),
317                            }
318                        } else {
319                            assert_eq!(bits, $f::MANTISSA_WIDTH + 1);
320                            mantissa.clear_bit($f::MANTISSA_WIDTH);
321                            (
322                                $f::from_raw_mantissa_and_exponent(
323                                    u64::exact_from(&mantissa),
324                                    u64::exact_from(exponent + $f::MAX_EXPONENT),
325                                ),
326                                o,
327                            )
328                        }
329                    } else if exponent >= $f::MIN_EXPONENT {
330                        let target_width = u64::wrapping_from(exponent - $f::MIN_EXPONENT + 1);
331                        let x = value >> $f::MIN_EXPONENT;
332                        let (n, d) = x.into_numerator_and_denominator();
333                        let (mantissa, o) = n.div_round(d, rm);
334                        (
335                            if mantissa.significant_bits() > target_width
336                                && exponent == $f::MIN_NORMAL_EXPONENT - 1
337                            {
338                                $f::MIN_POSITIVE_NORMAL
339                            } else {
340                                $f::from_raw_mantissa_and_exponent(u64::exact_from(&mantissa), 0)
341                            },
342                            o,
343                        )
344                    } else {
345                        match rm {
346                            Exact => {
347                                panic!("Value cannot be represented exactly as a float")
348                            }
349                            Floor | Down => (0.0, Less),
350                            Nearest => {
351                                if exponent == $f::MIN_EXPONENT - 1
352                                    && !abs_is_neg_power_of_2(&value)
353                                {
354                                    ($f::MIN_POSITIVE_SUBNORMAL, Greater)
355                                } else {
356                                    (0.0, Less)
357                                }
358                            }
359                            _ => ($f::MIN_POSITIVE_SUBNORMAL, Greater),
360                        }
361                    };
362                    if value.sign {
363                        (f, o)
364                    } else {
365                        (-f, o.reverse())
366                    }
367                }
368            }
369        }
370
371        impl TryFrom<&Rational> for $f {
372            type Error = FloatConversionError;
373
374            /// Converts a [`Rational`] to a primitive float, taking the [`Rational`] by reference.
375            /// If the input isn't exactly equal to any float, an error is returned.
376            ///
377            /// # Worst-case complexity
378            /// $T(n) = O(n)$
379            ///
380            /// $M(n) = O(n)$
381            ///
382            /// where $T$ is time, $M$ is additional memory, and $n$ is `value.significant_bits()`.
383            ///
384            /// # Examples
385            /// See [here](super::primitive_float_from_rational#try_from).
386            fn try_from(value: &Rational) -> Result<$f, Self::Error> {
387                if *value == 0 {
388                    Ok(0.0)
389                } else {
390                    let (mantissa, exponent, _) = value
391                        .sci_mantissa_and_exponent_round_ref(Exact)
392                        .ok_or(FloatConversionError::Inexact)?;
393                    let f = $f::from_sci_mantissa_and_exponent(mantissa, i64::exact_from(exponent))
394                        .ok_or(FloatConversionError::Inexact);
395                    if value.sign { f } else { f.map(|x| -x) }
396                }
397            }
398        }
399
400        impl ConvertibleFrom<&Rational> for $f {
401            /// Determines whether a [`Rational`] can be exactly converted to a primitive float,
402            /// taking the [`Rational`] by reference.
403            ///
404            /// # Worst-case complexity
405            /// $T(n) = O(n)$
406            ///
407            /// $M(n) = O(n)$
408            ///
409            /// where $T$ is time, $M$ is additional memory, and $n$ is `value.significant_bits()`.
410            ///
411            /// # Examples
412            /// See [here](super::primitive_float_from_rational#convertible_from).
413            fn convertible_from(value: &Rational) -> bool {
414                if *value == 0 {
415                    true
416                } else {
417                    if let Some((mantissa, exponent, _)) =
418                        value.sci_mantissa_and_exponent_round_ref::<$f>(Exact)
419                    {
420                        let exponent = i64::exact_from(exponent);
421                        if !($f::MIN_EXPONENT..=$f::MAX_EXPONENT).contains(&exponent) {
422                            return false;
423                        }
424                        let (orig_mantissa, orig_exponent) = mantissa.raw_mantissa_and_exponent();
425                        orig_exponent == u64::wrapping_from($f::MAX_EXPONENT)
426                            && exponent >= $f::MIN_NORMAL_EXPONENT
427                            || orig_mantissa.divisible_by_power_of_2(u64::wrapping_from(
428                                $f::MIN_NORMAL_EXPONENT - exponent,
429                            ))
430                    } else {
431                        false
432                    }
433                }
434            }
435        }
436    };
437}
438apply_to_primitive_floats!(float_impls);