Skip to main content

dashu_float/
convert.rs

1use core::{
2    convert::{TryFrom, TryInto},
3    num::FpCategory,
4};
5
6use dashu_base::{
7    Approximation::*, BitTest, ConversionError, DivRemEuclid, EstimatedLog2, FloatEncoding, Sign,
8    Signed,
9};
10use dashu_int::{IBig, UBig, Word};
11
12use crate::{
13    error::{assert_finite, panic_unlimited_precision},
14    fbig::FBig,
15    repr::{Context, Repr},
16    round::{
17        mode::{HalfAway, HalfEven, Zero},
18        Round, Rounded, Rounding,
19    },
20    utils::{ilog_exact, shl_digits, shl_digits_in_place, shr_digits},
21};
22
23impl<R: Round> Context<R> {
24    /// Convert an [IBig] instance to a [FBig] instance with precision
25    /// and rounding given by the context.
26    ///
27    /// # Examples
28    ///
29    /// ```
30    /// # use core::str::FromStr;
31    /// # use dashu_base::ParseError;
32    /// # use dashu_float::DBig;
33    /// use dashu_base::Approximation::*;
34    /// use dashu_float::{Context, round::{mode::HalfAway, Rounding::*}};
35    ///
36    /// let context = Context::<HalfAway>::new(2);
37    /// assert_eq!(context.convert_int::<10>((-12).into()), Exact(DBig::from_str("-12")?));
38    /// assert_eq!(
39    ///     context.convert_int::<10>(5678.into()),
40    ///     Inexact(DBig::from_str("5.7e3")?, AddOne)
41    /// );
42    /// # Ok::<(), ParseError>(())
43    /// ```
44    pub fn convert_int<const B: Word>(&self, n: IBig) -> Rounded<FBig<R, B>> {
45        let repr = Repr::<B>::new(n, 0);
46        self.repr_round(repr).map(|v| FBig::new(v, *self))
47    }
48}
49
50macro_rules! impl_from_float_for_fbig {
51    ($t:ty) => {
52        impl TryFrom<$t> for Repr<2> {
53            type Error = ConversionError;
54
55            fn try_from(f: $t) -> Result<Self, Self::Error> {
56                match f.decode() {
57                    Ok((man, exp)) => Ok(Repr::new(man.into(), exp as _)),
58                    Err(FpCategory::Infinite) => match f.sign() {
59                        Sign::Positive => Ok(Self::infinity()),
60                        Sign::Negative => Ok(Self::neg_infinity()),
61                    },
62                    _ => Err(ConversionError::OutOfBounds), // NaN
63                }
64            }
65        }
66
67        impl<R: Round> TryFrom<$t> for FBig<R, 2> {
68            type Error = ConversionError;
69
70            fn try_from(f: $t) -> Result<Self, Self::Error> {
71                match f.decode() {
72                    Ok((man, exp)) => {
73                        let repr = Repr::new(man.into(), exp as _);
74
75                        // The precision is inferenced from the mantissa, because the mantissa of
76                        // normal float is always normalized. This will produce correct precision
77                        // for subnormal floats
78                        let bits = man.unsigned_abs().bit_len();
79                        let context = Context::new(bits);
80                        Ok(Self::new(repr, context))
81                    }
82                    Err(FpCategory::Infinite) => match f.sign() {
83                        Sign::Positive => Ok(Self::INFINITY),
84                        Sign::Negative => Ok(Self::NEG_INFINITY),
85                    },
86                    _ => Err(ConversionError::OutOfBounds), // NaN
87                }
88            }
89        }
90    };
91}
92
93impl_from_float_for_fbig!(f32);
94impl_from_float_for_fbig!(f64);
95
96impl<R: Round, const B: Word> FBig<R, B> {
97    /// Convert the float number to base 10 (with decimal exponents) rounding to even
98    /// and tying away from zero.
99    ///
100    /// It's equivalent to `self.with_rounding::<HalfAway>().with_base::<10>()`.
101    /// The output is directly of type [DBig][crate::DBig].
102    ///
103    /// See [with_base()][Self::with_base] for the precision behavior.
104    ///
105    /// # Examples
106    ///
107    /// ```
108    /// # use core::str::FromStr;
109    /// # use dashu_base::ParseError;
110    /// # use dashu_float::{FBig, DBig};
111    /// use dashu_base::Approximation::*;
112    /// use dashu_float::round::Rounding::*;
113    ///
114    /// type Real = FBig;
115    ///
116    /// assert_eq!(
117    ///     Real::from_str("0x1234")?.to_decimal(),
118    ///     Exact(DBig::from_str("4660")?)
119    /// );
120    /// assert_eq!(
121    ///     Real::from_str("0x12.34")?.to_decimal(),
122    ///     Inexact(DBig::from_str("18.20")?, NoOp)
123    /// );
124    /// assert_eq!(
125    ///     Real::from_str("0x1.234p-4")?.to_decimal(),
126    ///     Inexact(DBig::from_str("0.07111")?, AddOne)
127    /// );
128    /// # Ok::<(), ParseError>(())
129    /// ```
130    ///
131    /// # Panics
132    ///
133    /// Panics if the associated context has unlimited precision and the conversion
134    /// cannot be performed losslessly.
135    #[inline]
136    pub fn to_decimal(&self) -> Rounded<FBig<HalfAway, 10>> {
137        self.clone().with_rounding().with_base::<10>()
138    }
139
140    /// Convert the float number to base 2 (with binary exponents) rounding towards zero.
141    ///
142    /// It's equivalent to `self.with_rounding::<Zero>().with_base::<2>()`.
143    ///
144    /// See [with_base()][Self::with_base] for the precision and rounding behavior.
145    ///
146    /// # Examples
147    ///
148    /// ```
149    /// # use core::str::FromStr;
150    /// # use dashu_base::ParseError;
151    /// # use dashu_float::{FBig, DBig};
152    /// use dashu_base::Approximation::*;
153    /// use dashu_float::round::{mode::HalfAway, Rounding::*};
154    ///
155    /// type Real = FBig;
156    ///
157    /// assert_eq!(
158    ///     DBig::from_str("1234")?.to_binary(),
159    ///     Exact(Real::from_str("0x4d2")?)
160    /// );
161    /// assert_eq!(
162    ///     DBig::from_str("12.34")?.to_binary(),
163    ///     Inexact(Real::from_str("0xc.57")?, NoOp)
164    /// );
165    /// assert_eq!(
166    ///     DBig::from_str("1.234e-1")?.to_binary(),
167    ///     Inexact(Real::from_str("0x1.f97p-4")?, NoOp)
168    /// );
169    /// # Ok::<(), ParseError>(())
170    /// ```
171    ///
172    /// # Panics
173    ///
174    /// Panics if the associated context has unlimited precision and the conversion
175    /// cannot be performed losslessly.
176    #[inline]
177    pub fn to_binary(&self) -> Rounded<FBig<Zero, 2>> {
178        self.clone().with_rounding().with_base::<2>()
179    }
180
181    /// Explicitly change the precision of the float number.
182    ///
183    /// If the given precision is less than the current value in the context,
184    /// it will be rounded with the rounding mode specified by the generic parameter.
185    ///
186    /// # Examples
187    ///
188    /// ```rust
189    /// # use core::str::FromStr;
190    /// # use dashu_base::ParseError;
191    /// # use dashu_float::{FBig, DBig};
192    /// use dashu_base::Approximation::*;
193    /// use dashu_float::round::{mode::HalfAway, Rounding::*};
194    ///
195    /// let a = DBig::from_str("2.345")?;
196    /// assert_eq!(a.precision(), 4);
197    /// assert_eq!(
198    ///     a.clone().with_precision(3),
199    ///     Inexact(DBig::from_str("2.35")?, AddOne)
200    /// );
201    /// assert_eq!(
202    ///     a.clone().with_precision(5),
203    ///     Exact(DBig::from_str("2.345")?)
204    /// );
205    /// # Ok::<(), ParseError>(())
206    /// ```
207    #[inline]
208    pub fn with_precision(self, precision: usize) -> Rounded<Self> {
209        let new_context = Context::new(precision);
210
211        // shrink if necessary
212        let repr = if self.context.precision > precision {
213            // it also handles unlimited precision
214            new_context.repr_round(self.repr)
215        } else {
216            Exact(self.repr)
217        };
218
219        repr.map(|v| Self::new(v, new_context))
220    }
221
222    /// Explicitly change the rounding mode of the number.
223    ///
224    /// This operation doesn't modify the underlying representation, it only changes
225    /// the rounding mode in the context.
226    ///
227    /// # Examples
228    ///
229    /// ```rust
230    /// # use core::str::FromStr;
231    /// # use dashu_base::ParseError;
232    /// # use dashu_float::{FBig, DBig};
233    /// use dashu_base::Approximation::*;
234    /// use dashu_float::round::{mode::{HalfAway, Zero}, Rounding::*};
235    ///
236    /// type DBigHalfAway = DBig;
237    /// type DBigZero = FBig::<Zero, 10>;
238    ///
239    /// let a = DBigHalfAway::from_str("2.345")?;
240    /// let b = DBigZero::from_str("2.345")?;
241    /// assert_eq!(a.with_rounding::<Zero>(), b);
242    /// # Ok::<(), ParseError>(())
243    /// ```
244    #[inline]
245    pub fn with_rounding<NewR: Round>(self) -> FBig<NewR, B> {
246        FBig {
247            repr: self.repr,
248            context: Context::new(self.context.precision),
249        }
250    }
251
252    /// Explicitly change the base of the float number.
253    ///
254    /// This function internally calls [with_base_and_precision][Self::with_base_and_precision].
255    /// The precision of the result number will be calculated in such a way that the new
256    /// limit of the significand is less than or equal to before. That is, the new precision
257    /// will be the max integer such that
258    ///
259    /// `NewB ^ new_precision <= B ^ old_precision`
260    ///
261    /// If any rounding happens during the conversion, it follows the rounding mode specified
262    /// by the generic parameter.
263    ///
264    /// # Examples
265    ///
266    /// ```rust
267    /// # use core::str::FromStr;
268    /// # use dashu_base::ParseError;
269    /// # use dashu_float::{FBig, DBig};
270    /// use dashu_base::Approximation::*;
271    /// use dashu_float::round::{mode::Zero, Rounding::*};
272    ///
273    /// type FBin = FBig;
274    /// type FDec = FBig<Zero, 10>;
275    /// type FHex = FBig<Zero, 16>;
276    ///
277    /// let a = FBin::from_str("0x1.234")?; // 0x1234 * 2^-12
278    /// assert_eq!(
279    ///     a.clone().with_base::<10>(),
280    ///     // 1.1376953125 rounded towards zero
281    ///     Inexact(FDec::from_str("1.137")?, NoOp)
282    /// );
283    /// assert_eq!(
284    ///     a.clone().with_base::<16>(),
285    ///     // conversion is exact when the new base is a power of the old base
286    ///     Exact(FHex::from_str("1.234")?)
287    /// );
288    /// # Ok::<(), ParseError>(())
289    /// ```
290    ///
291    /// # Panics
292    ///
293    /// Panics if the associated context has unlimited precision and the conversion
294    /// cannot be performed losslessly.
295    #[inline]
296    #[allow(non_upper_case_globals)]
297    pub fn with_base<const NewB: Word>(self) -> Rounded<FBig<R, NewB>> {
298        // if self.context.precision is zero, then precision is also zero
299        let precision =
300            Repr::<B>::BASE.pow(self.context.precision).log2_bounds().0 / NewB.log2_bounds().1;
301        self.with_base_and_precision(precision as usize)
302    }
303
304    /// Explicitly change the base of the float number with given precision (under the new base).
305    ///
306    /// Infinities are mapped to infinities inexactly, the error will be [NoOp][Rounding::NoOp].
307    ///
308    /// Conversion for float numbers with unlimited precision is only allowed in following cases:
309    /// - The number is infinite
310    /// - The new base NewB is a power of B
311    /// - B is a power of the new base NewB
312    ///
313    /// # Examples
314    ///
315    /// ```rust
316    /// # use core::str::FromStr;
317    /// # use dashu_base::ParseError;
318    /// # use dashu_float::{FBig, DBig};
319    /// use dashu_base::Approximation::*;
320    /// use dashu_float::round::{mode::Zero, Rounding::*};
321    ///
322    /// type FBin = FBig;
323    /// type FDec = FBig<Zero, 10>;
324    /// type FHex = FBig<Zero, 16>;
325    ///
326    /// let a = FBin::from_str("0x1.234")?; // 0x1234 * 2^-12
327    /// assert_eq!(
328    ///     a.clone().with_base_and_precision::<10>(8),
329    ///     // 1.1376953125 rounded towards zero
330    ///     Inexact(FDec::from_str("1.1376953")?, NoOp)
331    /// );
332    /// assert_eq!(
333    ///     a.clone().with_base_and_precision::<16>(8),
334    ///     // conversion can be exact when the new base is a power of the old base
335    ///     Exact(FHex::from_str("1.234")?)
336    /// );
337    /// assert_eq!(
338    ///     a.clone().with_base_and_precision::<16>(2),
339    ///     // but the conversion is still inexact if the target precision is smaller
340    ///     Inexact(FHex::from_str("1.2")?, NoOp)
341    /// );
342    /// # Ok::<(), ParseError>(())
343    /// ```
344    ///
345    /// # Panics
346    ///
347    /// Panics if the associated context has unlimited precision and the conversion
348    /// cannot be performed losslessly.
349    #[allow(non_upper_case_globals)]
350    #[inline]
351    pub fn with_base_and_precision<const NewB: Word>(
352        self,
353        precision: usize,
354    ) -> Rounded<FBig<R, NewB>> {
355        let context = Context::<R>::new(precision);
356        context
357            .convert_base(self.repr)
358            .map(|repr| FBig::new(repr, context))
359    }
360
361    // TODO: implement quantize() (like the python decimal)
362
363    /// Convert the float number to integer with the given rounding mode.
364    ///
365    /// # Warning
366    ///
367    /// If the float number has a very large exponent, it will be evaluated and result
368    /// in allocating an huge integer and it might eat up all your memory.
369    ///
370    /// To get a rough idea of how big the number is, it's recommended to use [EstimatedLog2].
371    ///
372    /// # Examples
373    ///
374    /// ```
375    /// # use core::str::FromStr;
376    /// # use dashu_base::ParseError;
377    /// # use dashu_float::{FBig, DBig};
378    /// use dashu_base::Approximation::*;
379    /// use dashu_float::round::Rounding::*;
380    ///
381    /// assert_eq!(
382    ///     DBig::from_str("1234")?.to_int(),
383    ///     Exact(1234.into())
384    /// );
385    /// assert_eq!(
386    ///     DBig::from_str("1.234e6")?.to_int(),
387    ///     Exact(1234000.into())
388    /// );
389    /// assert_eq!(
390    ///     DBig::from_str("1.234")?.to_int(),
391    ///     Inexact(1.into(), NoOp)
392    /// );
393    /// # Ok::<(), ParseError>(())
394    /// ```
395    ///
396    /// # Panics
397    ///
398    /// Panics if the number is infinte
399    pub fn to_int(&self) -> Rounded<IBig> {
400        assert_finite(&self.repr);
401
402        // shortcut when the number is already an integer
403        if self.repr.exponent >= 0 {
404            return Exact(shl_digits::<B>(&self.repr.significand, self.repr.exponent as usize));
405        }
406
407        let (hi, lo, precision) = self.split_at_point_internal();
408        let adjust = R::round_fract::<B>(&hi, lo, precision);
409        Inexact(hi + adjust, adjust)
410    }
411
412    /// Convert the float number to [f32] with the rounding mode associated with the type.
413    ///
414    /// Note that the conversion is inexact even if the number is infinite.
415    ///
416    /// # Examples
417    ///
418    /// ```
419    /// # use core::str::FromStr;
420    /// # use dashu_base::ParseError;
421    /// # use dashu_float::DBig;
422    /// assert_eq!(DBig::from_str("1.234")?.to_f32().value(), 1.234);
423    /// assert_eq!(DBig::INFINITY.to_f32().value(), f32::INFINITY);
424    /// # Ok::<(), ParseError>(())
425    /// ```
426    #[inline]
427    pub fn to_f32(&self) -> Rounded<f32> {
428        if self.repr.is_infinite() {
429            return Inexact(self.sign() * f32::INFINITY, Rounding::NoOp);
430        }
431
432        let context = Context::<R>::new(24);
433        if B != 2 {
434            let rounded: Rounded<Repr<2>> = context.convert_base(self.repr.clone());
435            rounded.and_then(|v| v.into_f32_internal())
436        } else {
437            context
438                .repr_round_ref(&self.repr)
439                .and_then(|v| v.into_f32_internal())
440        }
441    }
442
443    /// Convert the float number to [f64] with [HalfEven] rounding mode regardless of the mode associated with this number.
444    ///
445    /// Note that the conversion is inexact even if the number is infinite.
446    ///
447    /// # Examples
448    ///
449    /// ```
450    /// # use core::str::FromStr;
451    /// # use dashu_base::ParseError;
452    /// # use dashu_float::DBig;
453    /// assert_eq!(DBig::from_str("1.234")?.to_f64().value(), 1.234);
454    /// assert_eq!(DBig::INFINITY.to_f64().value(), f64::INFINITY);
455    /// # Ok::<(), ParseError>(())
456    /// ```
457    #[inline]
458    pub fn to_f64(&self) -> Rounded<f64> {
459        if self.repr.is_infinite() {
460            return Inexact(self.sign() * f64::INFINITY, Rounding::NoOp);
461        }
462
463        let context = Context::<HalfEven>::new(53);
464        if B != 2 {
465            let rounded: Rounded<Repr<2>> = context.convert_base(self.repr.clone());
466            rounded.and_then(|v| v.into_f64_internal())
467        } else {
468            context
469                .repr_round_ref(&self.repr)
470                .and_then(|v| v.into_f64_internal())
471        }
472    }
473}
474
475impl<R: Round> Context<R> {
476    // Convert the [Repr] from base B to base NewB, with the precision under the target base from this context.
477    #[allow(non_upper_case_globals)]
478    fn convert_base<const B: Word, const NewB: Word>(&self, repr: Repr<B>) -> Rounded<Repr<NewB>> {
479        // shortcut if NewB is the same as B
480        if NewB == B {
481            return Exact(Repr {
482                significand: repr.significand,
483                exponent: repr.exponent,
484            });
485        }
486
487        // shortcut for infinities, no rounding happens but the result is inexact
488        if repr.is_infinite() {
489            return Inexact(
490                Repr {
491                    significand: repr.significand,
492                    exponent: repr.exponent,
493                },
494                Rounding::NoOp,
495            );
496        }
497
498        if NewB > B {
499            // shortcut if NewB is a power of B
500            let n = ilog_exact(NewB, B);
501            if n > 1 {
502                let (exp, rem) = repr.exponent.div_rem_euclid(n as isize);
503                let signif = repr.significand * B.pow(rem as u32);
504                let repr = Repr::new(signif, exp);
505                return self.repr_round(repr);
506            }
507        } else {
508            // shortcut if B is a power of NewB
509            let n = ilog_exact(B, NewB);
510            if n > 1 {
511                let exp = repr.exponent * n as isize;
512                return Exact(Repr::new(repr.significand, exp));
513            }
514        }
515
516        // if the base cannot be converted losslessly, the precision must be set
517        if self.precision == 0 {
518            panic_unlimited_precision();
519        }
520
521        // XXX: there's a potential optimization: if B is a multiple of NewB, then the factor B
522        // should be trivially removed first, but this requires full support of const generics.
523
524        // choose a exponent threshold such that number with exponent smaller than this value
525        // will be converted by directly evaluating the power. The threshold here is chosen such
526        // that the power under base 10 will fit in a double word.
527        const THRESHOLD_SMALL_EXP: isize = (Word::BITS as f32 * 0.60206) as isize; // word bits * 2 / log2(10)
528        if repr.exponent.abs() <= THRESHOLD_SMALL_EXP {
529            // if the exponent is small enough, directly evaluate the exponent
530            if repr.exponent >= 0 {
531                let signif = repr.significand * Repr::<B>::BASE.pow(repr.exponent as usize);
532                Exact(Repr::new(signif, 0))
533            } else {
534                let num = Repr::new(repr.significand, 0);
535                let den = Repr::new(Repr::<B>::BASE.pow(-repr.exponent as usize).into(), 0);
536                self.repr_div(num, den)
537            }
538        } else {
539            // if the exponent is large, then we first estimate the result exponent as floor(exponent * log(B) / log(NewB)),
540            // then the fractional part is multiplied with the original significand
541            let work_context = Context::<R>::new(2 * self.precision); // double the precision to get the precise logarithm
542            let new_exp = repr.exponent
543                * work_context
544                    .ln(&Repr::new(Repr::<B>::BASE.into(), 0))
545                    .value();
546            let (exponent, rem) = new_exp.div_rem_euclid(work_context.ln_base::<NewB>());
547            let exponent: isize = exponent.try_into().unwrap();
548            let exp_rem = rem.exp();
549            let significand = repr.significand * exp_rem.repr.significand;
550            let repr = Repr::new(significand, exponent + exp_rem.repr.exponent);
551            self.repr_round(repr)
552        }
553    }
554}
555
556impl<const B: Word> Repr<B> {
557    // this method requires that the representation is already rounded to 24 binary bits
558    fn into_f32_internal(self) -> Rounded<f32> {
559        assert!(B == 2);
560        debug_assert!(self.is_finite());
561        debug_assert!(self.significand.bit_len() <= 24);
562
563        let sign = self.sign();
564        let man24: i32 = self.significand.try_into().unwrap();
565        if self.exponent >= 128 {
566            // max f32 = 2^128 * (1 - 2^-24)
567            match sign {
568                Sign::Positive => Inexact(f32::INFINITY, Rounding::AddOne),
569                Sign::Negative => Inexact(f32::NEG_INFINITY, Rounding::SubOne),
570            }
571        } else if self.exponent < -149 - 24 {
572            // min f32 = 2^-149
573            Inexact(sign * 0f32, Rounding::NoOp)
574        } else {
575            match f32::encode(man24, self.exponent as i16) {
576                Exact(v) => Exact(v),
577                // this branch only happens when the result underflows
578                Inexact(v, _) => Inexact(v, Rounding::NoOp),
579            }
580        }
581    }
582
583    /// Convert the float number representation to a [f32] with the default IEEE 754 rounding mode.
584    ///
585    /// The default IEEE 754 rounding mode is [HalfEven] (rounding to nearest, ties to even). To convert
586    /// the float number with a specific rounding mode, please use [FBig::to_f32].
587    ///
588    /// # Examples
589    ///
590    /// ```
591    /// # use dashu_base::Approximation::*;
592    /// # use dashu_float::{Repr, round::Rounding::*};
593    /// assert_eq!(Repr::<2>::one().to_f32(), Exact(1.0));
594    /// assert_eq!(Repr::<10>::infinity().to_f32(), Inexact(f32::INFINITY, NoOp));
595    /// ```
596    #[inline]
597    pub fn to_f32(&self) -> Rounded<f32> {
598        // Note: the implementation here should be kept consistent with FBig::to_f32
599
600        if self.is_infinite() {
601            return Inexact(self.sign() * f32::INFINITY, Rounding::NoOp);
602        }
603
604        let context = Context::<HalfEven>::new(24);
605        if B != 2 {
606            let rounded: Rounded<Repr<2>> = context.convert_base(self.clone());
607            rounded.and_then(|v| v.into_f32_internal())
608        } else {
609            context
610                .repr_round_ref(self)
611                .and_then(|v| v.into_f32_internal())
612        }
613    }
614
615    // this method requires that the representation is already rounded to 53 binary bits
616    fn into_f64_internal(self) -> Rounded<f64> {
617        assert!(B == 2);
618        debug_assert!(self.is_finite());
619        debug_assert!(self.significand.bit_len() <= 53);
620
621        let sign = self.sign();
622        let man53: i64 = self.significand.try_into().unwrap();
623        if self.exponent >= 1024 {
624            // max f64 = 2^1024 × (1 − 2^−53)
625            match sign {
626                Sign::Positive => Inexact(f64::INFINITY, Rounding::AddOne),
627                Sign::Negative => Inexact(f64::NEG_INFINITY, Rounding::SubOne),
628            }
629        } else if self.exponent < -1074 - 53 {
630            // min f64 = 2^-1074
631            Inexact(sign * 0f64, Rounding::NoOp)
632        } else {
633            match f64::encode(man53, self.exponent as i16) {
634                Exact(v) => Exact(v),
635                // this branch only happens when the result underflows
636                Inexact(v, _) => Inexact(v, Rounding::NoOp),
637            }
638        }
639    }
640
641    /// Convert the float number representation to a [f64] with the default IEEE 754 rounding mode.
642    ///
643    /// The default IEEE 754 rounding mode is [HalfEven] (rounding to nearest, ties to even). To convert
644    /// the float number with a specific rounding mode, please use [FBig::to_f64].
645    ///
646    /// # Examples
647    ///
648    /// ```
649    /// # use dashu_base::Approximation::*;
650    /// # use dashu_float::{Repr, round::Rounding::*};
651    /// assert_eq!(Repr::<2>::one().to_f64(), Exact(1.0));
652    /// assert_eq!(Repr::<10>::infinity().to_f64(), Inexact(f64::INFINITY, NoOp));
653    /// ```
654    #[inline]
655    pub fn to_f64(&self) -> Rounded<f64> {
656        // Note: the implementation here should be kept consistent with FBig::to_f64
657
658        if self.is_infinite() {
659            return Inexact(self.sign() * f64::INFINITY, Rounding::NoOp);
660        }
661
662        let context = Context::<HalfEven>::new(53);
663        if B != 2 {
664            let rounded: Rounded<Repr<2>> = context.convert_base(self.clone());
665            rounded.and_then(|v| v.into_f64_internal())
666        } else {
667            context
668                .repr_round_ref(self)
669                .and_then(|v| v.into_f64_internal())
670        }
671    }
672
673    /// Convert the float number representation to a [IBig].
674    ///
675    /// The fractional part is always rounded to zero. To convert with other rounding modes,
676    /// please use [FBig::to_int()].
677    ///
678    /// # Warning
679    ///
680    /// If the float number has a very large exponent, it will be evaluated and result
681    /// in allocating an huge integer and it might eat up all your memory.
682    ///
683    /// To get a rough idea of how big the number is, it's recommended to use [EstimatedLog2].
684    ///
685    /// # Examples
686    ///
687    /// ```
688    /// # use dashu_base::Approximation::*;
689    /// # use dashu_int::IBig;
690    /// # use dashu_float::{Repr, round::Rounding::*};
691    /// assert_eq!(Repr::<2>::neg_one().to_int(), Exact(IBig::NEG_ONE));
692    /// ```
693    ///
694    /// # Panics
695    ///
696    /// Panics if the number is infinte.
697    pub fn to_int(&self) -> Rounded<IBig> {
698        assert_finite(self);
699
700        if self.exponent >= 0 {
701            // the number is already an integer
702            Exact(shl_digits::<B>(&self.significand, self.exponent as usize))
703        } else if self.smaller_than_one() {
704            // the number is definitely smaller than
705            Inexact(IBig::ZERO, Rounding::NoOp)
706        } else {
707            let int = shr_digits::<B>(&self.significand, (-self.exponent) as usize);
708            Inexact(int, Rounding::NoOp)
709        }
710    }
711}
712
713impl<const B: Word> From<UBig> for Repr<B> {
714    #[inline]
715    fn from(n: UBig) -> Self {
716        Self::new(n.into(), 0)
717    }
718}
719impl<R: Round, const B: Word> From<UBig> for FBig<R, B> {
720    #[inline]
721    fn from(n: UBig) -> Self {
722        Self::from_parts(n.into(), 0)
723    }
724}
725
726impl<const B: Word> From<IBig> for Repr<B> {
727    #[inline]
728    fn from(n: IBig) -> Self {
729        Self::new(n, 0)
730    }
731}
732impl<R: Round, const B: Word> From<IBig> for FBig<R, B> {
733    #[inline]
734    fn from(n: IBig) -> Self {
735        Self::from_parts(n, 0)
736    }
737}
738
739impl<R: Round, const B: Word> TryFrom<FBig<R, B>> for IBig {
740    type Error = ConversionError;
741
742    #[inline]
743    fn try_from(value: FBig<R, B>) -> Result<Self, Self::Error> {
744        if value.repr.is_infinite() {
745            Err(ConversionError::OutOfBounds)
746        } else if value.repr.exponent < 0 {
747            Err(ConversionError::LossOfPrecision)
748        } else {
749            let mut int = value.repr.significand;
750            shl_digits_in_place::<B>(&mut int, value.repr.exponent as usize);
751            Ok(int)
752        }
753    }
754}
755
756impl<R: Round, const B: Word> TryFrom<FBig<R, B>> for UBig {
757    type Error = ConversionError;
758
759    #[inline]
760    fn try_from(value: FBig<R, B>) -> Result<Self, Self::Error> {
761        let int: IBig = value.try_into()?;
762        int.try_into()
763    }
764}
765
766macro_rules! fbig_unsigned_conversions {
767    ($($t:ty)*) => {$(
768        impl<const B: Word> From<$t> for Repr<B> {
769            #[inline]
770            fn from(value: $t) -> Repr<B> {
771                UBig::from(value).into()
772            }
773        }
774        impl<R: Round, const B: Word> From<$t> for FBig<R, B> {
775            #[inline]
776            fn from(value: $t) -> FBig<R, B> {
777                UBig::from(value).into()
778            }
779        }
780
781        impl<const B: Word> TryFrom<Repr<B>> for $t {
782            type Error = ConversionError;
783
784            fn try_from(value: Repr<B>) -> Result<Self, Self::Error> {
785                if value.sign() == Sign::Negative || value.is_infinite() {
786                    Err(ConversionError::OutOfBounds)
787                } else {
788                    let (log2_lb, _) = value.log2_bounds();
789                    if log2_lb >= <$t>::BITS as f32 {
790                        Err(ConversionError::OutOfBounds)
791                    } else if value.exponent < 0 {
792                        Err(ConversionError::LossOfPrecision)
793                    } else {
794                        shl_digits::<B>(&value.significand, value.exponent as usize).try_into()
795                    }
796                }
797            }
798        }
799        impl<R: Round, const B: Word> TryFrom<FBig<R, B>> for $t {
800            type Error = ConversionError;
801
802            #[inline]
803            fn try_from(value: FBig<R, B>) -> Result<Self, Self::Error> {
804                value.repr.try_into()
805            }
806        }
807    )*};
808}
809fbig_unsigned_conversions!(u8 u16 u32 u64 u128 usize);
810
811macro_rules! fbig_signed_conversions {
812    ($($t:ty)*) => {$(
813        impl<R: Round, const B: Word> From<$t> for FBig<R, B> {
814            #[inline]
815            fn from(value: $t) -> FBig<R, B> {
816                IBig::from(value).into()
817            }
818        }
819
820        impl<R: Round, const B: Word> TryFrom<FBig<R, B>> for $t {
821            type Error = ConversionError;
822
823            fn try_from(value: FBig<R, B>) -> Result<Self, Self::Error> {
824                if value.repr.is_infinite() {
825                    Err(ConversionError::OutOfBounds)
826                } else {
827                    let (log2_lb, _) = value.repr.log2_bounds();
828                    if log2_lb >= <$t>::BITS as f32 {
829                        Err(ConversionError::OutOfBounds)
830                    } else if value.repr.exponent < 0 {
831                        Err(ConversionError::LossOfPrecision)
832                    } else {
833                        shl_digits::<B>(&value.repr.significand, value.repr.exponent as usize).try_into()
834                    }
835                }
836            }
837        }
838    )*};
839}
840fbig_signed_conversions!(i8 i16 i32 i64 i128 isize);
841
842macro_rules! impl_from_fbig_for_float {
843    ($t:ty, $method:ident) => {
844        impl TryFrom<Repr<2>> for $t {
845            type Error = ConversionError;
846
847            #[inline]
848            fn try_from(value: Repr<2>) -> Result<Self, Self::Error> {
849                if value.is_infinite() {
850                    Err(ConversionError::LossOfPrecision)
851                } else {
852                    match value.$method() {
853                        Exact(v) => Ok(v),
854                        Inexact(v, _) => {
855                            if v.is_infinite() {
856                                Err(ConversionError::OutOfBounds)
857                            } else {
858                                Err(ConversionError::LossOfPrecision)
859                            }
860                        }
861                    }
862                }
863            }
864        }
865
866        impl<R: Round> TryFrom<FBig<R, 2>> for $t {
867            type Error = ConversionError;
868
869            #[inline]
870            fn try_from(value: FBig<R, 2>) -> Result<Self, Self::Error> {
871                // this method is the same as the one for Repr, but it has to be re-implemented
872                // because the rounding behavior of to_32/to_64 is different.
873                if value.repr.is_infinite() {
874                    Err(ConversionError::LossOfPrecision)
875                } else {
876                    match value.$method() {
877                        Exact(v) => Ok(v),
878                        Inexact(v, _) => {
879                            if v.is_infinite() {
880                                Err(ConversionError::OutOfBounds)
881                            } else {
882                                Err(ConversionError::LossOfPrecision)
883                            }
884                        }
885                    }
886                }
887            }
888        }
889    };
890}
891impl_from_fbig_for_float!(f32, to_f32);
892impl_from_fbig_for_float!(f64, to_f64);