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::{factor_base, 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    /// Convert the float number to integer with the given rounding mode.
362    ///
363    /// # Warning
364    ///
365    /// If the float number has a very large exponent, it will be evaluated and result
366    /// in allocating an huge integer and it might eat up all your memory.
367    ///
368    /// To get a rough idea of how big the number is, it's recommended to use [EstimatedLog2].
369    ///
370    /// # Examples
371    ///
372    /// ```
373    /// # use core::str::FromStr;
374    /// # use dashu_base::ParseError;
375    /// # use dashu_float::{FBig, DBig};
376    /// use dashu_base::Approximation::*;
377    /// use dashu_float::round::Rounding::*;
378    ///
379    /// assert_eq!(
380    ///     DBig::from_str("1234")?.to_int(),
381    ///     Exact(1234.into())
382    /// );
383    /// assert_eq!(
384    ///     DBig::from_str("1.234e6")?.to_int(),
385    ///     Exact(1234000.into())
386    /// );
387    /// assert_eq!(
388    ///     DBig::from_str("1.234")?.to_int(),
389    ///     Inexact(1.into(), NoOp)
390    /// );
391    /// # Ok::<(), ParseError>(())
392    /// ```
393    ///
394    /// # Panics
395    ///
396    /// Panics if the number is infinte
397    pub fn to_int(&self) -> Rounded<IBig> {
398        assert_finite(&self.repr);
399
400        // shortcut when the number is already an integer
401        if self.repr.exponent >= 0 {
402            return Exact(shl_digits::<B>(&self.repr.significand, self.repr.exponent as usize));
403        }
404
405        let (hi, lo, precision) = self.split_at_point_internal();
406        let adjust = R::round_fract::<B>(&hi, lo, precision);
407        Inexact(hi + adjust, adjust)
408    }
409
410    /// Convert the float number to [f32] with the rounding mode associated with the type.
411    ///
412    /// Note that the conversion is inexact even if the number is infinite.
413    ///
414    /// # Examples
415    ///
416    /// ```
417    /// # use core::str::FromStr;
418    /// # use dashu_base::ParseError;
419    /// # use dashu_float::DBig;
420    /// assert_eq!(DBig::from_str("1.234")?.to_f32().value(), 1.234);
421    /// assert_eq!(DBig::INFINITY.to_f32().value(), f32::INFINITY);
422    /// # Ok::<(), ParseError>(())
423    /// ```
424    #[inline]
425    pub fn to_f32(&self) -> Rounded<f32> {
426        if self.repr.is_infinite() {
427            return Inexact(self.sign() * f32::INFINITY, Rounding::NoOp);
428        }
429
430        let context = Context::<R>::new(24);
431        context
432            .convert_base::<B, 2>(self.repr.clone())
433            .and_then(|v| context.repr_round_ref(&v))
434            .and_then(|v| v.into_f32_internal())
435    }
436
437    /// Convert the float number to [f64] with [HalfEven] rounding mode regardless of the mode associated with this number.
438    ///
439    /// Note that the conversion is inexact even if the number is infinite.
440    ///
441    /// # Examples
442    ///
443    /// ```
444    /// # use core::str::FromStr;
445    /// # use dashu_base::ParseError;
446    /// # use dashu_float::DBig;
447    /// assert_eq!(DBig::from_str("1.234")?.to_f64().value(), 1.234);
448    /// assert_eq!(DBig::INFINITY.to_f64().value(), f64::INFINITY);
449    /// # Ok::<(), ParseError>(())
450    /// ```
451    #[inline]
452    pub fn to_f64(&self) -> Rounded<f64> {
453        if self.repr.is_infinite() {
454            return Inexact(self.sign() * f64::INFINITY, Rounding::NoOp);
455        }
456
457        let context = Context::<R>::new(53);
458        context
459            .convert_base::<B, 2>(self.repr.clone())
460            .and_then(|v| context.repr_round_ref(&v))
461            .and_then(|v| v.into_f64_internal())
462    }
463}
464
465impl<R: Round> Context<R> {
466    // Convert the [Repr] from base B to base NewB, with the precision under the target base from this context.
467    #[allow(non_upper_case_globals)]
468    fn convert_base<const B: Word, const NewB: Word>(&self, repr: Repr<B>) -> Rounded<Repr<NewB>> {
469        // shortcut if NewB is the same as B
470        if NewB == B {
471            return Exact(Repr {
472                significand: repr.significand,
473                exponent: repr.exponent,
474            });
475        }
476
477        // shortcut for infinities, no rounding happens but the result is inexact
478        if repr.is_infinite() {
479            return Inexact(
480                Repr {
481                    significand: repr.significand,
482                    exponent: repr.exponent,
483                },
484                Rounding::NoOp,
485            );
486        }
487
488        if NewB > B {
489            // shortcut if NewB is a power of B
490            let n = ilog_exact(NewB, B);
491            if n > 1 {
492                let (exp, rem) = repr.exponent.div_rem_euclid(n as isize);
493                let signif = repr.significand * B.pow(rem as u32);
494                let repr = Repr::new(signif, exp);
495                return self.repr_round(repr);
496            }
497        } else {
498            // shortcut if B is a power of NewB
499            let n = ilog_exact(B, NewB);
500            if n > 1 {
501                let exp = repr.exponent * n as isize;
502                return Exact(Repr::new(repr.significand, exp));
503            }
504        }
505
506        // Shortcut: when B and NewB share common factors, factor out the common part.
507        // B = NewB^a * r where gcd(r, NewB) = 1, so B^exp = NewB^(a*exp) * r^exp.
508        // For positive exponents the result is always exact (integer multiplication).
509        // For negative exponents, exact only when r^|exp| divides the significand.
510        let (a, r) = factor_base(B, NewB);
511        if a > 0 && r > 1 {
512            if repr.exponent >= 0 {
513                let r_exp = UBig::from_word(r).pow(repr.exponent as usize);
514                let significand = repr.significand * r_exp;
515                let new_repr = Repr::<NewB>::new(significand, a as isize * repr.exponent);
516                return self.repr_round(new_repr);
517            } else {
518                let r_exp: IBig = UBig::from_word(r).pow((-repr.exponent) as usize).into();
519                if repr.significand.is_multiple_of(&r_exp) {
520                    let new_repr =
521                        Repr::<NewB>::new(repr.significand / r_exp, a as isize * repr.exponent);
522                    return self.repr_round(new_repr);
523                }
524            }
525        }
526
527        // When NewB is a multiple of B: compute significand * B^exp directly
528        // as an integer, then express in base NewB.
529        if NewB % B == 0 && repr.exponent >= 0 {
530            let signif = repr.significand * Repr::<B>::BASE.pow(repr.exponent as usize);
531            let new_repr = Repr::<NewB>::new(signif, 0);
532            return self.repr_round(new_repr);
533        }
534
535        // if the base cannot be converted losslessly, the precision must be set
536        if self.precision == 0 {
537            panic_unlimited_precision();
538        }
539
540        // choose a exponent threshold such that number with exponent smaller than this value
541        // will be converted by directly evaluating the power. The threshold here is chosen such
542        // that the power under base 10 will fit in a double word.
543        const THRESHOLD_SMALL_EXP: isize = (Word::BITS as f32 * 0.60206) as isize; // word bits * 2 / log2(10)
544        if repr.exponent.abs() <= THRESHOLD_SMALL_EXP {
545            // if the exponent is small enough, directly evaluate the exponent
546            if repr.exponent >= 0 {
547                let signif = repr.significand * Repr::<B>::BASE.pow(repr.exponent as usize);
548                Exact(Repr::new(signif, 0))
549            } else {
550                let num = Repr::new(repr.significand, 0);
551                let den = Repr::new(Repr::<B>::BASE.pow(-repr.exponent as usize).into(), 0);
552                self.repr_div(num, den)
553            }
554        } else {
555            // if the exponent is large, then we first estimate the result exponent as floor(exponent * log(B) / log(NewB)),
556            // then the fractional part is multiplied with the original significand
557            let work_context = Context::<R>::new(2 * self.precision); // double the precision to get the precise logarithm
558            let new_exp = repr.exponent
559                * work_context
560                    .ln(&Repr::new(Repr::<B>::BASE.into(), 0))
561                    .value();
562            let (exponent, rem) = new_exp.div_rem_euclid(work_context.ln_base::<NewB>());
563            let exponent: isize = exponent.try_into().unwrap();
564            let exp_rem = rem.exp();
565            let significand = repr.significand * exp_rem.repr.significand;
566            let repr = Repr::new(significand, exponent + exp_rem.repr.exponent);
567            self.repr_round(repr)
568        }
569    }
570}
571
572impl<const B: Word> Repr<B> {
573    // this method requires that the representation is already rounded to 24 binary bits
574    fn into_f32_internal(self) -> Rounded<f32> {
575        assert!(B == 2);
576        debug_assert!(self.is_finite());
577        debug_assert!(self.significand.bit_len() <= 24);
578
579        let sign = self.sign();
580        let man24: i32 = self.significand.try_into().unwrap();
581        if self.exponent >= 128 {
582            // max f32 = 2^128 * (1 - 2^-24)
583            match sign {
584                Sign::Positive => Inexact(f32::INFINITY, Rounding::AddOne),
585                Sign::Negative => Inexact(f32::NEG_INFINITY, Rounding::SubOne),
586            }
587        } else if self.exponent < -149 - 24 {
588            // min f32 = 2^-149
589            Inexact(sign * 0f32, Rounding::NoOp)
590        } else {
591            match f32::encode(man24, self.exponent as i16) {
592                Exact(v) => Exact(v),
593                // this branch only happens when the result underflows
594                Inexact(v, _) => Inexact(v, Rounding::NoOp),
595            }
596        }
597    }
598
599    /// Convert the float number representation to a [f32] with the default IEEE 754 rounding mode.
600    ///
601    /// The default IEEE 754 rounding mode is [HalfEven] (rounding to nearest, ties to even). To convert
602    /// the float number with a specific rounding mode, please use [FBig::to_f32].
603    ///
604    /// # Examples
605    ///
606    /// ```
607    /// # use dashu_base::Approximation::*;
608    /// # use dashu_float::{Repr, round::Rounding::*};
609    /// assert_eq!(Repr::<2>::one().to_f32(), Exact(1.0));
610    /// assert_eq!(Repr::<10>::infinity().to_f32(), Inexact(f32::INFINITY, NoOp));
611    /// ```
612    #[inline]
613    pub fn to_f32(&self) -> Rounded<f32> {
614        if self.is_infinite() {
615            return Inexact(self.sign() * f32::INFINITY, Rounding::NoOp);
616        }
617
618        let context = Context::<HalfEven>::new(24);
619        context
620            .convert_base::<B, 2>(self.clone())
621            .and_then(|v| context.repr_round_ref(&v))
622            .and_then(|v| v.into_f32_internal())
623    }
624
625    // this method requires that the representation is already rounded to 53 binary bits
626    fn into_f64_internal(self) -> Rounded<f64> {
627        assert!(B == 2);
628        debug_assert!(self.is_finite());
629        debug_assert!(self.significand.bit_len() <= 53);
630
631        let sign = self.sign();
632        let man53: i64 = self.significand.try_into().unwrap();
633        if self.exponent >= 1024 {
634            // max f64 = 2^1024 × (1 − 2^−53)
635            match sign {
636                Sign::Positive => Inexact(f64::INFINITY, Rounding::AddOne),
637                Sign::Negative => Inexact(f64::NEG_INFINITY, Rounding::SubOne),
638            }
639        } else if self.exponent < -1074 - 53 {
640            // min f64 = 2^-1074
641            Inexact(sign * 0f64, Rounding::NoOp)
642        } else {
643            match f64::encode(man53, self.exponent as i16) {
644                Exact(v) => Exact(v),
645                // this branch only happens when the result underflows
646                Inexact(v, _) => Inexact(v, Rounding::NoOp),
647            }
648        }
649    }
650
651    /// Convert the float number representation to a [f64] with the default IEEE 754 rounding mode.
652    ///
653    /// The default IEEE 754 rounding mode is [HalfEven] (rounding to nearest, ties to even). To convert
654    /// the float number with a specific rounding mode, please use [FBig::to_f64].
655    ///
656    /// # Examples
657    ///
658    /// ```
659    /// # use dashu_base::Approximation::*;
660    /// # use dashu_float::{Repr, round::Rounding::*};
661    /// assert_eq!(Repr::<2>::one().to_f64(), Exact(1.0));
662    /// assert_eq!(Repr::<10>::infinity().to_f64(), Inexact(f64::INFINITY, NoOp));
663    /// ```
664    #[inline]
665    pub fn to_f64(&self) -> Rounded<f64> {
666        if self.is_infinite() {
667            return Inexact(self.sign() * f64::INFINITY, Rounding::NoOp);
668        }
669
670        let context = Context::<HalfEven>::new(53);
671        context
672            .convert_base::<B, 2>(self.clone())
673            .and_then(|v| context.repr_round_ref(&v))
674            .and_then(|v| v.into_f64_internal())
675    }
676
677    /// Convert the float number representation to a [IBig].
678    ///
679    /// The fractional part is always rounded to zero. To convert with other rounding modes,
680    /// please use [FBig::to_int()].
681    ///
682    /// # Warning
683    ///
684    /// If the float number has a very large exponent, it will be evaluated and result
685    /// in allocating an huge integer and it might eat up all your memory.
686    ///
687    /// To get a rough idea of how big the number is, it's recommended to use [EstimatedLog2].
688    ///
689    /// # Examples
690    ///
691    /// ```
692    /// # use dashu_base::Approximation::*;
693    /// # use dashu_int::IBig;
694    /// # use dashu_float::{Repr, round::Rounding::*};
695    /// assert_eq!(Repr::<2>::neg_one().to_int(), Exact(IBig::NEG_ONE));
696    /// ```
697    ///
698    /// # Panics
699    ///
700    /// Panics if the number is infinte.
701    pub fn to_int(&self) -> Rounded<IBig> {
702        assert_finite(self);
703
704        if self.exponent >= 0 {
705            // the number is already an integer
706            Exact(shl_digits::<B>(&self.significand, self.exponent as usize))
707        } else if self.smaller_than_one() {
708            // the number is definitely smaller than
709            Inexact(IBig::ZERO, Rounding::NoOp)
710        } else {
711            let int = shr_digits::<B>(&self.significand, (-self.exponent) as usize);
712            Inexact(int, Rounding::NoOp)
713        }
714    }
715}
716
717impl<const B: Word> From<UBig> for Repr<B> {
718    #[inline]
719    fn from(n: UBig) -> Self {
720        Self::new(n.into(), 0)
721    }
722}
723impl<R: Round, const B: Word> From<UBig> for FBig<R, B> {
724    #[inline]
725    fn from(n: UBig) -> Self {
726        Self::from_parts(n.into(), 0)
727    }
728}
729
730impl<const B: Word> From<IBig> for Repr<B> {
731    #[inline]
732    fn from(n: IBig) -> Self {
733        Self::new(n, 0)
734    }
735}
736impl<R: Round, const B: Word> From<IBig> for FBig<R, B> {
737    #[inline]
738    fn from(n: IBig) -> Self {
739        Self::from_parts(n, 0)
740    }
741}
742
743impl<R: Round, const B: Word> TryFrom<FBig<R, B>> for IBig {
744    type Error = ConversionError;
745
746    #[inline]
747    fn try_from(value: FBig<R, B>) -> Result<Self, Self::Error> {
748        if value.repr.is_infinite() {
749            Err(ConversionError::OutOfBounds)
750        } else if value.repr.exponent < 0 {
751            Err(ConversionError::LossOfPrecision)
752        } else {
753            let mut int = value.repr.significand;
754            shl_digits_in_place::<B>(&mut int, value.repr.exponent as usize);
755            Ok(int)
756        }
757    }
758}
759
760impl<R: Round, const B: Word> TryFrom<FBig<R, B>> for UBig {
761    type Error = ConversionError;
762
763    #[inline]
764    fn try_from(value: FBig<R, B>) -> Result<Self, Self::Error> {
765        let int: IBig = value.try_into()?;
766        int.try_into()
767    }
768}
769
770macro_rules! fbig_unsigned_conversions {
771    ($($t:ty)*) => {$(
772        impl<const B: Word> From<$t> for Repr<B> {
773            #[inline]
774            fn from(value: $t) -> Repr<B> {
775                UBig::from(value).into()
776            }
777        }
778        impl<R: Round, const B: Word> From<$t> for FBig<R, B> {
779            #[inline]
780            fn from(value: $t) -> FBig<R, B> {
781                UBig::from(value).into()
782            }
783        }
784
785        impl<const B: Word> TryFrom<Repr<B>> for $t {
786            type Error = ConversionError;
787
788            fn try_from(value: Repr<B>) -> Result<Self, Self::Error> {
789                if value.sign() == Sign::Negative || value.is_infinite() {
790                    Err(ConversionError::OutOfBounds)
791                } else {
792                    let (log2_lb, _) = value.log2_bounds();
793                    if log2_lb >= <$t>::BITS as f32 {
794                        Err(ConversionError::OutOfBounds)
795                    } else if value.exponent < 0 {
796                        Err(ConversionError::LossOfPrecision)
797                    } else {
798                        shl_digits::<B>(&value.significand, value.exponent as usize).try_into()
799                    }
800                }
801            }
802        }
803        impl<R: Round, const B: Word> TryFrom<FBig<R, B>> for $t {
804            type Error = ConversionError;
805
806            #[inline]
807            fn try_from(value: FBig<R, B>) -> Result<Self, Self::Error> {
808                value.repr.try_into()
809            }
810        }
811    )*};
812}
813fbig_unsigned_conversions!(u8 u16 u32 u64 u128 usize);
814
815macro_rules! fbig_signed_conversions {
816    ($($t:ty)*) => {$(
817        impl<R: Round, const B: Word> From<$t> for FBig<R, B> {
818            #[inline]
819            fn from(value: $t) -> FBig<R, B> {
820                IBig::from(value).into()
821            }
822        }
823
824        impl<R: Round, const B: Word> TryFrom<FBig<R, B>> for $t {
825            type Error = ConversionError;
826
827            fn try_from(value: FBig<R, B>) -> Result<Self, Self::Error> {
828                if value.repr.is_infinite() {
829                    Err(ConversionError::OutOfBounds)
830                } else {
831                    let (log2_lb, _) = value.repr.log2_bounds();
832                    if log2_lb >= <$t>::BITS as f32 {
833                        Err(ConversionError::OutOfBounds)
834                    } else if value.repr.exponent < 0 {
835                        Err(ConversionError::LossOfPrecision)
836                    } else {
837                        shl_digits::<B>(&value.repr.significand, value.repr.exponent as usize).try_into()
838                    }
839                }
840            }
841        }
842    )*};
843}
844fbig_signed_conversions!(i8 i16 i32 i64 i128 isize);
845
846macro_rules! impl_from_fbig_for_float {
847    ($t:ty, $method:ident) => {
848        impl TryFrom<Repr<2>> for $t {
849            type Error = ConversionError;
850
851            #[inline]
852            fn try_from(value: Repr<2>) -> Result<Self, Self::Error> {
853                if value.is_infinite() {
854                    Err(ConversionError::LossOfPrecision)
855                } else {
856                    match value.$method() {
857                        Exact(v) => Ok(v),
858                        Inexact(v, _) => {
859                            if v.is_infinite() {
860                                Err(ConversionError::OutOfBounds)
861                            } else {
862                                Err(ConversionError::LossOfPrecision)
863                            }
864                        }
865                    }
866                }
867            }
868        }
869
870        impl<R: Round> TryFrom<FBig<R, 2>> for $t {
871            type Error = ConversionError;
872
873            #[inline]
874            fn try_from(value: FBig<R, 2>) -> Result<Self, Self::Error> {
875                // this method is the same as the one for Repr, but it has to be re-implemented
876                // because the rounding behavior of to_32/to_64 is different.
877                if value.repr.is_infinite() {
878                    Err(ConversionError::LossOfPrecision)
879                } else {
880                    match value.$method() {
881                        Exact(v) => Ok(v),
882                        Inexact(v, _) => {
883                            if v.is_infinite() {
884                                Err(ConversionError::OutOfBounds)
885                            } else {
886                                Err(ConversionError::LossOfPrecision)
887                            }
888                        }
889                    }
890                }
891            }
892        }
893    };
894}
895impl_from_fbig_for_float!(f32, to_f32);
896impl_from_fbig_for_float!(f64, to_f64);