malachite_float/conversion/
rational_from_float.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::InnerFloat::{Finite, Zero};
10use crate::{Float, significand_bits};
11use malachite_base::num::basic::traits::Zero as ZeroTrait;
12use malachite_base::num::conversion::traits::ConvertibleFrom;
13use malachite_nz::integer::Integer;
14use malachite_q::Rational;
15
16#[derive(Clone, Copy, Debug, Eq, PartialEq)]
17pub struct RationalFromFloatError;
18
19impl TryFrom<Float> for Rational {
20    type Error = RationalFromFloatError;
21
22    /// Converts a [`Float`] to a [`Rational`], taking the [`Float`] by value. If the [`Float`] is
23    /// not finite, an error is returned.
24    ///
25    /// # Worst-case complexity
26    /// $T(n) = O(n)$
27    ///
28    /// $M(n) = O(n)$
29    ///
30    /// where $T$ is time, $M$ is additional memory, and $n$ is `x.complexity()`.
31    ///
32    /// # Examples
33    /// ```
34    /// use malachite_base::num::basic::traits::{Infinity, NaN, Zero};
35    /// use malachite_float::conversion::rational_from_float::RationalFromFloatError;
36    /// use malachite_float::Float;
37    /// use malachite_q::Rational;
38    ///
39    /// assert_eq!(Rational::try_from(Float::ZERO).unwrap(), 0);
40    /// assert_eq!(
41    ///     Rational::try_from(Float::from(1.5)).unwrap().to_string(),
42    ///     "3/2"
43    /// );
44    /// assert_eq!(
45    ///     Rational::try_from(Float::from(-1.5)).unwrap().to_string(),
46    ///     "-3/2"
47    /// );
48    ///
49    /// assert_eq!(
50    ///     Rational::try_from(Float::INFINITY),
51    ///     Err(RationalFromFloatError)
52    /// );
53    /// assert_eq!(Rational::try_from(Float::NAN), Err(RationalFromFloatError));
54    /// ```
55    fn try_from(x: Float) -> Result<Rational, Self::Error> {
56        match x {
57            float_either_zero!() => Ok(Rational::ZERO),
58            Float(Finite {
59                sign,
60                exponent,
61                significand,
62                ..
63            }) => {
64                let bits = significand_bits(&significand);
65                Ok(
66                    Rational::from(Integer::from_sign_and_abs(sign, significand))
67                        << (i128::from(exponent) - i128::from(bits)),
68                )
69            }
70            _ => Err(RationalFromFloatError),
71        }
72    }
73}
74
75impl TryFrom<&Float> for Rational {
76    type Error = RationalFromFloatError;
77
78    /// Converts a [`Float`] to a [`Rational`], taking the [`Float`] by reference. If the [`Float`]
79    /// is not finite, an error is returned.
80    ///
81    /// # Worst-case complexity
82    /// $T(n) = O(n)$
83    ///
84    /// $M(n) = O(n)$
85    ///
86    /// where $T$ is time, $M$ is additional memory, and $n$ is `x.complexity()`.
87    ///
88    /// # Examples
89    /// ```
90    /// use malachite_base::num::basic::traits::{Infinity, NaN, Zero};
91    /// use malachite_float::conversion::rational_from_float::RationalFromFloatError;
92    /// use malachite_float::Float;
93    /// use malachite_q::Rational;
94    ///
95    /// assert_eq!(Rational::try_from(&Float::ZERO).unwrap(), 0);
96    /// assert_eq!(
97    ///     Rational::try_from(&Float::from(1.5)).unwrap().to_string(),
98    ///     "3/2"
99    /// );
100    /// assert_eq!(
101    ///     Rational::try_from(&Float::from(-1.5)).unwrap().to_string(),
102    ///     "-3/2"
103    /// );
104    ///
105    /// assert_eq!(
106    ///     Rational::try_from(&Float::INFINITY),
107    ///     Err(RationalFromFloatError)
108    /// );
109    /// assert_eq!(Rational::try_from(&Float::NAN), Err(RationalFromFloatError));
110    /// ```
111    fn try_from(x: &Float) -> Result<Rational, Self::Error> {
112        match x {
113            float_either_zero!() => Ok(Rational::ZERO),
114            Float(Finite {
115                sign,
116                exponent,
117                significand,
118                ..
119            }) => {
120                let bits = significand_bits(significand);
121                Ok(
122                    Rational::from(Integer::from_sign_and_abs_ref(*sign, significand))
123                        << (i128::from(*exponent) - i128::from(bits)),
124                )
125            }
126            _ => Err(RationalFromFloatError),
127        }
128    }
129}
130
131impl ConvertibleFrom<&Float> for Rational {
132    /// Determines whether a [`Float`] can be converted to a [`Rational`] (which is when the
133    /// [`Float`] is finite), taking the [`Float`] by reference.
134    ///
135    /// # Worst-case complexity
136    /// Constant time and additional memory.
137    ///
138    /// # Examples
139    /// ```
140    /// use malachite_base::num::basic::traits::{Infinity, NaN, Zero};
141    /// use malachite_base::num::conversion::traits::ConvertibleFrom;
142    /// use malachite_float::Float;
143    /// use malachite_q::Rational;
144    ///
145    /// assert_eq!(Rational::convertible_from(&Float::ZERO), true);
146    /// assert_eq!(Rational::convertible_from(&Float::from(123.0)), true);
147    /// assert_eq!(Rational::convertible_from(&Float::from(-123.0)), true);
148    /// assert_eq!(Rational::convertible_from(&Float::from(1.5)), true);
149    ///
150    /// assert_eq!(Rational::convertible_from(&Float::INFINITY), false);
151    /// assert_eq!(Rational::convertible_from(&Float::NAN), false);
152    /// ```
153    #[inline]
154    fn convertible_from(x: &Float) -> bool {
155        x.is_finite()
156    }
157}