dashu_ratio/
convert.rs

1use core::cmp::Ordering;
2
3use dashu_base::{
4    Approximation::{self, *},
5    BitTest, ConversionError, DivRem, FloatEncoding, PowerOfTwo, Sign, UnsignedAbs,
6};
7use dashu_int::{IBig, UBig};
8
9use crate::{
10    rbig::{RBig, Relaxed},
11    repr::Repr,
12};
13
14impl From<UBig> for Repr {
15    #[inline]
16    fn from(v: UBig) -> Self {
17        Repr {
18            numerator: v.into(),
19            denominator: UBig::ONE,
20        }
21    }
22}
23
24impl From<IBig> for Repr {
25    #[inline]
26    fn from(v: IBig) -> Self {
27        Repr {
28            numerator: v,
29            denominator: UBig::ONE,
30        }
31    }
32}
33
34impl TryFrom<Repr> for UBig {
35    type Error = ConversionError;
36    #[inline]
37    fn try_from(value: Repr) -> Result<Self, Self::Error> {
38        let (sign, mag) = value.numerator.into_parts();
39        if sign == Sign::Negative {
40            Err(ConversionError::OutOfBounds)
41        } else if mag.is_one() {
42            Ok(mag)
43        } else {
44            Err(ConversionError::LossOfPrecision)
45        }
46    }
47}
48
49impl TryFrom<Repr> for IBig {
50    type Error = ConversionError;
51    #[inline]
52    fn try_from(value: Repr) -> Result<Self, Self::Error> {
53        if value.denominator.is_one() {
54            Ok(value.numerator)
55        } else {
56            Err(ConversionError::LossOfPrecision)
57        }
58    }
59}
60
61macro_rules! forward_conversion_to_repr {
62    ($from:ty => $t:ident) => {
63        impl From<$from> for $t {
64            #[inline]
65            fn from(v: $from) -> Self {
66                $t(Repr::from(v))
67            }
68        }
69        impl TryFrom<$t> for $from {
70            type Error = ConversionError;
71            #[inline]
72            fn try_from(value: $t) -> Result<Self, Self::Error> {
73                Self::try_from(value.0)
74            }
75        }
76    };
77}
78forward_conversion_to_repr!(UBig => RBig);
79forward_conversion_to_repr!(IBig => RBig);
80forward_conversion_to_repr!(UBig => Relaxed);
81forward_conversion_to_repr!(IBig => Relaxed);
82
83macro_rules! impl_conversion_for_prim_ints {
84    ($($t:ty)*) => {$(
85        impl From<$t> for Repr {
86            #[inline]
87            fn from(v: $t) -> Repr {
88                Repr {
89                    numerator: v.into(),
90                    denominator: UBig::ONE
91                }
92            }
93        }
94
95        impl TryFrom<Repr> for $t {
96            type Error = ConversionError;
97            #[inline]
98            fn try_from(value: Repr) -> Result<Self, Self::Error> {
99                let int: IBig = value.try_into()?;
100                int.try_into()
101            }
102        }
103
104        forward_conversion_to_repr!($t => RBig);
105        forward_conversion_to_repr!($t => Relaxed);
106    )*};
107}
108impl_conversion_for_prim_ints!(u8 u16 u32 u64 u128 usize i8 i16 i32 i64 i128 isize);
109
110macro_rules! impl_conversion_from_float {
111    ($t:ty) => {
112        impl TryFrom<$t> for Repr {
113            type Error = ConversionError;
114
115            fn try_from(value: $t) -> Result<Self, Self::Error> {
116                // shortcut to prevent issues in counting leading zeros
117                if value == 0. {
118                    return Ok(Repr::zero());
119                }
120
121                match value.decode() {
122                    Ok((man, exp)) => {
123                        // here we don't remove the common factor 2, because we need exact
124                        // exponent value in some cases (like approx_f32 and approx_f64)
125                        let repr = if exp >= 0 {
126                            Repr {
127                                numerator: IBig::from(man) << exp as usize,
128                                denominator: UBig::ONE,
129                            }
130                        } else {
131                            let mut denominator = UBig::ZERO;
132                            denominator.set_bit((-exp) as _);
133                            Repr {
134                                numerator: IBig::from(man),
135                                denominator,
136                            }
137                        };
138                        Ok(repr)
139                    }
140                    Err(_) => Err(ConversionError::OutOfBounds),
141                }
142            }
143        }
144
145        impl TryFrom<$t> for RBig {
146            type Error = ConversionError;
147            #[inline]
148            fn try_from(value: $t) -> Result<Self, Self::Error> {
149                Repr::try_from(value).map(|repr| RBig(repr.reduce2()))
150            }
151        }
152        impl TryFrom<$t> for Relaxed {
153            type Error = ConversionError;
154            #[inline]
155            fn try_from(value: $t) -> Result<Self, Self::Error> {
156                Repr::try_from(value).map(|repr| Relaxed(repr.reduce2()))
157            }
158        }
159    };
160}
161impl_conversion_from_float!(f32);
162impl_conversion_from_float!(f64);
163
164macro_rules! impl_conversion_to_float {
165    ($t:ty [$lb:literal, $ub:literal]) => {
166        impl TryFrom<RBig> for $t {
167            type Error = ConversionError;
168
169            /// Convert RBig to primitive floats. It returns [Ok] only if
170            /// the conversion can be done losslessly
171            fn try_from(value: RBig) -> Result<Self, Self::Error> {
172                if value.0.numerator.is_zero() {
173                    Ok(0.)
174                } else if value.0.denominator.is_power_of_two() {
175                    // conversion is exact only if the denominator is a power of two
176                    let num_bits = value.0.numerator.bit_len();
177                    let den_bits = value.0.denominator.trailing_zeros().unwrap();
178                    let top_bit = num_bits as isize - den_bits as isize;
179                    if top_bit > $ub {
180                        // see to_f32::encode for explanation of the bounds
181                        Err(ConversionError::OutOfBounds)
182                    } else if top_bit < $lb {
183                        Err(ConversionError::LossOfPrecision)
184                    } else {
185                        match <$t>::encode(
186                            value.0.numerator.try_into().unwrap(),
187                            -(den_bits as i16),
188                        ) {
189                            Exact(v) => Ok(v),
190                            Inexact(v, _) => {
191                                if v.is_infinite() {
192                                    Err(ConversionError::OutOfBounds)
193                                } else {
194                                    Err(ConversionError::LossOfPrecision)
195                                }
196                            }
197                        }
198                    }
199                } else {
200                    Err(ConversionError::LossOfPrecision)
201                }
202            }
203        }
204
205        impl TryFrom<Relaxed> for $t {
206            type Error = ConversionError;
207
208            #[inline]
209            fn try_from(value: Relaxed) -> Result<Self, Self::Error> {
210                // convert to RBig to eliminate cofactors
211                <$t>::try_from(value.canonicalize())
212            }
213        }
214    };
215}
216impl_conversion_to_float!(f32 [-149, 128]); // see f32::encode for explanation of the bounds
217impl_conversion_to_float!(f64 [-1074, 1024]); // see f32::encode for explanation of the bounds
218
219impl Repr {
220    /// Convert the rational number to [f32] without guaranteed correct rounding.
221    fn to_f32_fast(&self) -> f32 {
222        // shortcut
223        if self.numerator.is_zero() {
224            return 0.;
225        }
226
227        // to get enough precision (24 bits), we need to do a 48 by 24 bit division
228        let sign = self.numerator.sign();
229        let num_bits = self.numerator.bit_len();
230        let den_bits = self.denominator.bit_len();
231
232        let num_shift = num_bits as isize - 48;
233        let num48: i64 = if num_shift >= 0 {
234            (&self.numerator) >> num_shift as usize
235        } else {
236            (&self.numerator) << (-num_shift) as usize
237        }
238        .try_into()
239        .unwrap();
240
241        let den_shift = den_bits as isize - 24;
242        let den24: u32 = if den_shift >= 0 {
243            (&self.denominator) >> den_shift as usize
244        } else {
245            (&self.denominator) << (-den_shift) as usize
246        }
247        .try_into()
248        .unwrap();
249
250        // determine the exponent
251        let exponent = num_shift - den_shift;
252        if exponent >= 128 {
253            // max f32 = 2^128 * (1 - 2^-24)
254            sign * f32::INFINITY
255        } else if exponent < -149 - 25 {
256            // min f32 = 2^-149, quotient has at most 25 bits
257            sign * 0f32
258        } else {
259            let (mut man, r) = num48.unsigned_abs().div_rem(den24 as u64);
260
261            // round to nearest, ties to even
262            let half = (r as u32 * 2).cmp(&den24);
263            if half == Ordering::Greater || (half == Ordering::Equal && man & 1 > 0) {
264                man += 1;
265            }
266            f32::encode(sign * man as i32, exponent as i16).value()
267        }
268    }
269
270    fn to_f64_fast(&self) -> f64 {
271        // shortcut
272        if self.numerator.is_zero() {
273            return 0.;
274        }
275
276        // to get enough precision (53 bits), we need to do a 106 by 53 bit division
277        let sign = self.numerator.sign();
278        let num_bits = self.numerator.bit_len();
279        let den_bits = self.denominator.bit_len();
280
281        let num_shift = num_bits as isize - 106;
282        let num106: i128 = if num_shift >= 0 {
283            (&self.numerator) >> num_shift as usize
284        } else {
285            (&self.numerator) << (-num_shift) as usize
286        }
287        .try_into()
288        .unwrap();
289
290        let den_shift = den_bits as isize - 53;
291        let den53: u64 = if den_shift >= 0 {
292            (&self.denominator) >> den_shift as usize
293        } else {
294            (&self.denominator) << (-den_shift) as usize
295        }
296        .try_into()
297        .unwrap();
298
299        // determine the exponent
300        let exponent = num_shift - den_shift;
301        if exponent >= 1024 {
302            // max f64 = 2^1024 × (1 − 2^−53)
303            sign * f64::INFINITY
304        } else if exponent < -1074 - 54 {
305            // min f64 = 2^-1074, quotient has at most 54 bits
306            sign * 0f64
307        } else {
308            let (mut man, r) = num106.unsigned_abs().div_rem(den53 as u128);
309
310            // round to nearest, ties to even
311            let half = (r as u64 * 2).cmp(&den53);
312            if half == Ordering::Greater || (half == Ordering::Equal && man & 1 > 0) {
313                man += 1;
314            }
315            f64::encode(sign * man as i64, exponent as i16).value()
316        }
317    }
318
319    /// Convert the rational number to [f32] with guaranteed correct rounding.
320    fn to_f32(&self) -> Approximation<f32, Sign> {
321        // shortcut
322        if self.numerator.is_zero() {
323            return Exact(0.);
324        }
325
326        // to get enough precision, shift such that numerator has
327        // 24 bits more than the denominator
328        let sign = self.numerator.sign();
329        let num_bits = self.numerator.bit_len();
330        let den_bits = self.denominator.bit_len();
331
332        let shift = num_bits as isize - den_bits as isize - 24; // i.e. exponent
333        let (num, den) = if shift >= 0 {
334            (self.numerator.clone(), (&self.denominator) << shift as usize)
335        } else {
336            ((&self.numerator) << (-shift) as usize, self.denominator.clone())
337        };
338
339        // then construct the
340        if shift >= 128 {
341            // max f32 = 2^128 * (1 - 2^-24)
342            Inexact(sign * f32::INFINITY, sign)
343        } else if shift < -149 - 25 {
344            // min f32 = 2^-149, quotient has at most 25 bits
345            Inexact(sign * 0f32, -sign)
346        } else {
347            let (man, r) = num.unsigned_abs().div_rem(&den);
348            let man: u32 = man.try_into().unwrap();
349
350            // round to nearest, ties to even
351            if r.is_zero() {
352                Exact(man)
353            } else {
354                let half = (r << 1).cmp(&den);
355                if half == Ordering::Greater || (half == Ordering::Equal && man & 1 > 0) {
356                    Inexact(man + 1, sign)
357                } else {
358                    Inexact(man, -sign)
359                }
360            }
361            .and_then(|man| f32::encode(sign * man as i32, shift as i16))
362        }
363    }
364
365    fn to_f64(&self) -> Approximation<f64, Sign> {
366        // shortcut
367        if self.numerator.is_zero() {
368            return Exact(0.);
369        }
370
371        // to get enough precision, shift such that numerator has
372        // 53 bits more than the denominator
373        let sign = self.numerator.sign();
374        let num_bits = self.numerator.bit_len();
375        let den_bits = self.denominator.bit_len();
376
377        let shift = num_bits as isize - den_bits as isize - 53; // i.e. exponent
378        let (num, den) = if shift >= 0 {
379            (self.numerator.clone(), (&self.denominator) << shift as usize)
380        } else {
381            ((&self.numerator) << (-shift) as usize, self.denominator.clone())
382        };
383
384        // then construct the
385        if shift >= 1024 {
386            // max f64 = 2^1024 × (1 − 2^−53)
387            Inexact(sign * f64::INFINITY, sign)
388        } else if shift < -1074 - 53 {
389            // min f64 = 2^-1074, quotient has at most 53 bits
390            Inexact(sign * 0f64, -sign)
391        } else {
392            let (man, r) = num.unsigned_abs().div_rem(&den);
393            let man: u64 = man.try_into().unwrap();
394
395            // round to nearest, ties to even
396            if r.is_zero() {
397                Exact(man)
398            } else {
399                let half = (r << 1).cmp(&den);
400                if half == Ordering::Greater || (half == Ordering::Equal && man & 1 > 0) {
401                    Inexact(man + 1, sign)
402                } else {
403                    Inexact(man, -sign)
404                }
405            }
406            .and_then(|man| f64::encode(sign * man as i64, shift as i16))
407        }
408    }
409}
410
411impl RBig {
412    /// Convert the rational number to a [f32].
413    ///
414    /// The rounding follows the default IEEE 754 behavior (rounds to nearest,
415    /// ties to even).
416    ///
417    /// The rounding will be correct at most of the time, but in rare cases the
418    /// mantissa can be off by one bit. Use [RBig::to_f32] for ensured correct
419    /// rounding.
420    ///
421    /// # Examples
422    ///
423    /// ```
424    /// # use dashu_ratio::RBig;
425    /// assert_eq!(RBig::ONE.to_f32_fast(), 1f32);
426    ///
427    /// let r = RBig::from_parts(22.into(), 7u8.into());
428    /// assert_eq!(r.to_f32_fast(), 22./7.)
429    /// ```
430    #[inline]
431    pub fn to_f32_fast(&self) -> f32 {
432        self.0.to_f32_fast()
433    }
434
435    /// Convert the rational number to a [f64].
436    ///
437    /// The rounding follows the default IEEE 754 behavior (rounds to nearest,
438    /// ties to even).
439    ///
440    /// The rounding will be correct at most of the time, but in rare cases the
441    /// mantissa can be off by one bit. Use [RBig::to_f64] for ensured correct
442    /// rounding.
443    ///
444    /// # Examples
445    ///
446    /// ```
447    /// # use dashu_ratio::RBig;
448    /// assert_eq!(RBig::ONE.to_f64_fast(), 1f64);
449    ///
450    /// let r = RBig::from_parts(22.into(), 7u8.into());
451    /// assert_eq!(r.to_f64_fast(), 22./7.)
452    /// ```
453    #[inline]
454    pub fn to_f64_fast(&self) -> f64 {
455        self.0.to_f64_fast()
456    }
457
458    /// Convert the rational number to a [f32] with guaranteed correct rounding.
459    ///
460    /// The rounding follows the default IEEE 754 behavior (rounds to nearest,
461    /// ties to even).
462    ///
463    /// Because of the guaranteed rounding, it might take a long time to convert
464    /// when the numerator and denominator are large. In this case [RBig::to_f32_fast]
465    /// can be used if the correct rounding is not required.
466    ///
467    /// # Examples
468    ///
469    /// ```
470    /// # use dashu_base::{Approximation::*, Sign::*};
471    /// # use dashu_ratio::RBig;
472    /// assert_eq!(RBig::ONE.to_f32(), Exact(1f32));
473    ///
474    /// let r = RBig::from_parts(22.into(), 7u8.into());
475    /// // f32 representation of 22/7 is smaller than the actual 22/7
476    /// assert_eq!(r.to_f32(), Inexact(22./7., Negative));
477    /// ```
478    #[inline]
479    pub fn to_f32(&self) -> Approximation<f32, Sign> {
480        self.0.to_f32()
481    }
482
483    /// Convert the rational number to a [f64] with guaranteed correct rounding.
484    ///
485    /// The rounding follows the default IEEE 754 behavior (rounds to nearest,
486    /// ties to even).
487    ///
488    /// Because of the guaranteed rounding, it might take a long time to convert
489    /// when the numerator and denominator are large. In this case [RBig::to_f64_fast]
490    /// can be used if the correct rounding is not required.
491    ///
492    /// # Examples
493    ///
494    /// ```
495    /// # use dashu_base::{Approximation::*, Sign::*};
496    /// # use dashu_ratio::RBig;
497    /// assert_eq!(RBig::ONE.to_f64(), Exact(1f64));
498    ///
499    /// let r = RBig::from_parts(22.into(), 7u8.into());
500    /// // f64 representation of 22/7 is smaller than the actual 22/7
501    /// assert_eq!(r.to_f64(), Inexact(22./7., Negative));
502    /// ```
503    #[inline]
504    pub fn to_f64(&self) -> Approximation<f64, Sign> {
505        self.0.to_f64()
506    }
507
508    /// Convert the rational number to an [IBig].
509    ///
510    /// The conversion rounds toward zero. It's equivalent to [RBig::trunc],
511    /// but it returns the fractional part if the rational number is not an integer.
512    ///
513    /// # Examples
514    ///
515    /// ```
516    /// # use dashu_base::Approximation::*;
517    /// # use dashu_int::{IBig, UBig};
518    /// # use dashu_ratio::RBig;
519    /// let a = RBig::from_parts(22.into(), UBig::ONE);
520    /// assert_eq!(a.to_int(), Exact(IBig::from(22)));
521    ///
522    /// let b = RBig::from_parts(22.into(), 7u8.into());
523    /// assert_eq!(b.to_int(), Inexact(
524    ///     IBig::from(3), RBig::from_parts(1.into(), 7u8.into())
525    /// ));
526    /// ```
527    #[inline]
528    pub fn to_int(&self) -> Approximation<IBig, Self> {
529        let (trunc, fract) = self.clone().split_at_point();
530        if fract.is_zero() {
531            Approximation::Exact(trunc)
532        } else {
533            Approximation::Inexact(trunc, fract)
534        }
535    }
536}
537
538impl Relaxed {
539    /// Convert the rational number to a [f32].
540    ///
541    /// See [RBig::to_f32_fast] for details.
542    #[inline]
543    pub fn to_f32_fast(&self) -> f32 {
544        self.0.to_f32_fast()
545    }
546    /// Convert the rational number to a [f64].
547    ///
548    /// See [RBig::to_f64_fast] for details.
549    #[inline]
550    pub fn to_f64_fast(&self) -> f64 {
551        self.0.to_f64_fast()
552    }
553
554    /// Convert the rational number to a [f32] with guaranteed correct rounding.
555    ///
556    /// See [RBig::to_f32] for details.
557    #[inline]
558    pub fn to_f32(&self) -> Approximation<f32, Sign> {
559        self.0.to_f32()
560    }
561    /// Convert the rational number to a [f64] with guaranteed correct rounding.
562    ///
563    /// See [RBig::to_f64] for details.
564    #[inline]
565    pub fn to_f64(&self) -> Approximation<f64, Sign> {
566        self.0.to_f64()
567    }
568    /// Convert the rational number to am [IBig].
569    ///
570    /// See [RBig::to_int] for details.
571    #[inline]
572    pub fn to_int(&self) -> Approximation<IBig, Self> {
573        let (trunc, fract) = self.clone().split_at_point();
574        if fract.is_zero() {
575            Approximation::Exact(trunc)
576        } else {
577            Approximation::Inexact(trunc, fract)
578        }
579    }
580}