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