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