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);