dashu_ratio/third_party/
dashu_float.rs1use crate::{
2 rbig::{RBig, Relaxed},
3 repr::Repr,
4};
5use dashu_base::{Approximation, ConversionError, DivRem, Gcd};
6use dashu_float::{
7 round::{ErrorBounds, Round, Rounded},
8 Context, FBig, Repr as FBigRepr,
9};
10use dashu_int::{UBig, Word};
11
12impl<R: Round, const B: Word> From<Repr> for FBig<R, B> {
14 #[inline]
15 fn from(v: Repr) -> Self {
16 let Repr {
17 numerator,
18 denominator,
19 } = v;
20 FBig::from(numerator) / FBig::from(denominator)
21 }
22}
23
24#[allow(dead_code)]
26fn fbig_try_from_rbig<R: Round, const B: Word>(v: Repr) -> Result<FBig<R, B>, ConversionError> {
27 let Repr {
28 numerator,
29 denominator,
30 } = v;
31
32 let float_den = FBig::from(denominator);
33 let float_num = FBig::from(numerator);
34
35 if !float_num
37 .repr()
38 .significand()
39 .gcd(float_den.repr().significand())
40 .is_one()
41 {
42 return Err(ConversionError::LossOfPrecision);
43 }
44
45 Ok(float_den / float_num)
46}
47
48impl<const B: Word> TryFrom<FBigRepr<B>> for Repr {
49 type Error = ConversionError;
50 fn try_from(value: FBigRepr<B>) -> Result<Self, Self::Error> {
51 if value.is_infinite() {
52 Err(ConversionError::OutOfBounds)
53 } else {
54 let (signif, exp) = value.into_parts();
55 let (numerator, denominator) = if exp >= 0 {
56 (signif * UBig::from_word(B).pow(exp as usize), UBig::ONE)
57 } else {
58 (signif, UBig::from_word(B).pow((-exp) as usize))
59 };
60 Ok(Repr {
61 numerator,
62 denominator,
63 })
64 }
65 }
66}
67
68impl<R: Round, const B: Word> TryFrom<FBig<R, B>> for Repr {
69 type Error = ConversionError;
70 #[inline]
71 fn try_from(value: FBig<R, B>) -> Result<Self, Self::Error> {
72 value.into_repr().try_into()
73 }
74}
75
76macro_rules! forward_conversion_to_repr {
77 ($t:ident, $reduce:ident) => {
78 impl<R: Round, const B: Word> From<$t> for FBig<R, B> {
79 #[inline]
80 fn from(v: $t) -> Self {
81 v.0.into()
82 }
83 }
84
85 impl<const B: Word> TryFrom<FBigRepr<B>> for $t {
86 type Error = ConversionError;
87 #[inline]
88 fn try_from(value: FBigRepr<B>) -> Result<Self, Self::Error> {
89 Repr::try_from(value).map(|repr| $t(repr.$reduce()))
90 }
91 }
92
93 impl<R: Round, const B: Word> TryFrom<FBig<R, B>> for $t {
94 type Error = ConversionError;
95 #[inline]
96 fn try_from(value: FBig<R, B>) -> Result<Self, Self::Error> {
97 Repr::try_from(value).map(|repr| $t(repr.$reduce()))
98 }
99 }
100 };
101}
102forward_conversion_to_repr!(RBig, reduce);
103forward_conversion_to_repr!(Relaxed, reduce2);
104
105impl Repr {
106 fn to_float<R: Round, const B: Word>(&self, precision: usize) -> Rounded<FBig<R, B>> {
111 assert!(precision > 0);
112
113 if self.numerator.is_zero() {
114 return FBig::ZERO.with_precision(precision);
115 }
116
117 let base = UBig::from_word(B);
118 let num_digits = self.numerator.ilog(&base);
119 let den_digits = self.denominator.ilog(&base);
120
121 let shift;
122 let (q, r) = if num_digits >= precision + den_digits {
123 shift = 0;
124 (&self.numerator).div_rem(&self.denominator)
125 } else {
126 shift = (precision + den_digits) - num_digits;
127 if B == 2 {
128 (&self.numerator << shift).div_rem(&self.denominator)
129 } else {
130 (&self.numerator * base.pow(shift)).div_rem(&self.denominator)
131 }
132 };
133 let rounded = if r.is_zero() {
134 Approximation::Exact(q)
135 } else {
136 let adjust = R::round_ratio(&q, r, self.denominator.as_ibig());
137 Approximation::Inexact(q + adjust, adjust)
138 };
139
140 let context = Context::<R>::new(precision);
141 rounded
142 .and_then(|n| context.convert_int(n))
143 .map(|f| f >> (shift as isize))
144 }
145}
146
147impl RBig {
148 #[inline]
163 pub fn to_float<R: Round, const B: Word>(&self, precision: usize) -> Rounded<FBig<R, B>> {
164 self.0.to_float(precision)
165 }
166
167 pub fn simplest_from_float<R: ErrorBounds, const B: Word>(f: &FBig<R, B>) -> Option<Self> {
182 if f.repr().is_infinite() {
183 return None;
184 } else if f.repr().is_zero() {
185 return Some(Self::ZERO);
186 }
187
188 let (l, r, incl_l, incl_r) = R::error_bounds(f);
190 let lb = f - l.with_precision(f.precision() + 1).unwrap();
191 let rb = f + r.with_precision(f.precision() + 1).unwrap();
192
193 let left = Self::try_from(lb).unwrap();
195 let right = Self::try_from(rb).unwrap();
196 let mut simplest = Self::simplest_in(left.clone(), right.clone());
197 if incl_l && left.is_simpler_than(&simplest) {
198 simplest = left;
199 }
200 if incl_r && right.is_simpler_than(&simplest) {
201 simplest = right;
202 }
203 Some(simplest)
204 }
205}
206
207impl Relaxed {
208 #[inline]
212 pub fn to_float<R: Round, const B: Word>(&self, precision: usize) -> Rounded<FBig<R, B>> {
213 self.0.to_float(precision)
214 }
215}