malachite_q/conversion/
from_float_simplest.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 crate::arithmetic::traits::SimplestRationalInInterval;
11use crate::conversion::from_primitive_float::RationalFromPrimitiveFloatError;
12use malachite_base::num::basic::floats::PrimitiveFloat;
13use malachite_base::num::conversion::traits::ExactFrom;
14
15impl Rational {
16    /// Converts a primitive float to the simplest [`Rational`] that rounds to that value.
17    ///
18    /// To be more specific: Suppose the floating-point input is $x$. If $x$ is an integer, its
19    /// [`Rational`] equivalent is returned. Otherwise, this function finds $a$ and $b$, which are
20    /// the floating point predecessor and successor of $x$, and finds the simplest [`Rational`] in
21    /// the open interval $(\frac{x + a}{2}, \frac{x + b}{2})$. "Simplicity" refers to low
22    /// complexity. See [`Rational::cmp_complexity`] for a definition of complexity.
23    ///
24    /// For example, `0.1f32` is converted to $1/10$ rather than to the exact value of the float,
25    /// which is $13421773/134217728$. If you want the exact value, use `Rational::from` instead.
26    ///
27    /// If the floating point value is `NaN` or infinite, an error is returned.
28    ///
29    /// # Worst-case complexity
30    /// $T(n) = O(n^2 \log n \log\log n)$
31    ///
32    /// $M(n) = O(n \log n)$
33    ///
34    /// where $T$ is time, $M$ is additional memory, and $n$ is `x.sci_exponent()`.
35    ///
36    /// # Examples
37    /// ```
38    /// use malachite_base::strings::ToDebugString;
39    /// use malachite_q::conversion::from_primitive_float::RationalFromPrimitiveFloatError;
40    /// use malachite_q::Rational;
41    ///
42    /// assert_eq!(
43    ///     Rational::try_from_float_simplest(0.0).to_debug_string(),
44    ///     "Ok(0)"
45    /// );
46    /// assert_eq!(
47    ///     Rational::try_from_float_simplest(1.5).to_debug_string(),
48    ///     "Ok(3/2)"
49    /// );
50    /// assert_eq!(
51    ///     Rational::try_from_float_simplest(-1.5).to_debug_string(),
52    ///     "Ok(-3/2)"
53    /// );
54    /// assert_eq!(
55    ///     Rational::try_from_float_simplest(0.1f32).to_debug_string(),
56    ///     "Ok(1/10)"
57    /// );
58    /// assert_eq!(
59    ///     Rational::try_from_float_simplest(0.33333334f32).to_debug_string(),
60    ///     "Ok(1/3)"
61    /// );
62    /// assert_eq!(
63    ///     Rational::try_from_float_simplest(f32::NAN),
64    ///     Err(RationalFromPrimitiveFloatError)
65    /// );
66    /// ```
67    pub fn try_from_float_simplest<T: PrimitiveFloat>(
68        x: T,
69    ) -> Result<Self, RationalFromPrimitiveFloatError>
70    where
71        Self: TryFrom<T, Error = RationalFromPrimitiveFloatError>,
72    {
73        let q = Self::try_from(x)?;
74        Ok(if *q.denominator_ref() <= 2u32 {
75            q
76        } else {
77            let succ_q = Self::exact_from(x.next_higher());
78            let pred_q = Self::exact_from(x.next_lower());
79            let x = (pred_q + &q) >> 1;
80            let y = (succ_q + q) >> 1;
81            Self::simplest_rational_in_open_interval(&x, &y)
82        })
83    }
84}