1use core::cmp::Ordering;
2
3use dashu_base::{
4 Approximation::{self, *},
5 BitTest, ConversionError, DivRem, FloatEncoding, PowerOfTwo, Sign, UnsignedAbs,
6};
7use dashu_int::{IBig, UBig};
8
9use crate::{
10 rbig::{RBig, Relaxed},
11 repr::Repr,
12};
13
14impl From<UBig> for Repr {
15 #[inline]
16 fn from(v: UBig) -> Self {
17 Repr {
18 numerator: v.into(),
19 denominator: UBig::ONE,
20 }
21 }
22}
23
24impl From<IBig> for Repr {
25 #[inline]
26 fn from(v: IBig) -> Self {
27 Repr {
28 numerator: v,
29 denominator: UBig::ONE,
30 }
31 }
32}
33
34impl TryFrom<Repr> for UBig {
35 type Error = ConversionError;
36 #[inline]
37 fn try_from(value: Repr) -> Result<Self, Self::Error> {
38 if !value.denominator.is_one() {
40 return Err(ConversionError::LossOfPrecision);
41 }
42 let (sign, mag) = value.numerator.into_parts();
43 if sign == Sign::Negative {
44 Err(ConversionError::OutOfBounds)
45 } else {
46 Ok(mag)
47 }
48 }
49}
50
51impl TryFrom<Repr> for IBig {
52 type Error = ConversionError;
53 #[inline]
54 fn try_from(value: Repr) -> Result<Self, Self::Error> {
55 if value.denominator.is_one() {
56 Ok(value.numerator)
57 } else {
58 Err(ConversionError::LossOfPrecision)
59 }
60 }
61}
62
63macro_rules! forward_conversion_to_repr {
64 ($from:ty => $t:ident) => {
65 impl From<$from> for $t {
66 #[inline]
67 fn from(v: $from) -> Self {
68 $t(Repr::from(v))
69 }
70 }
71 impl TryFrom<$t> for $from {
72 type Error = ConversionError;
73 #[inline]
74 fn try_from(value: $t) -> Result<Self, Self::Error> {
75 Self::try_from(value.0)
76 }
77 }
78 };
79}
80forward_conversion_to_repr!(UBig => RBig);
81forward_conversion_to_repr!(IBig => RBig);
82forward_conversion_to_repr!(UBig => Relaxed);
83forward_conversion_to_repr!(IBig => Relaxed);
84
85macro_rules! impl_conversion_for_prim_ints {
86 ($($t:ty)*) => {$(
87 impl From<$t> for Repr {
88 #[inline]
89 fn from(v: $t) -> Repr {
90 Repr {
91 numerator: v.into(),
92 denominator: UBig::ONE
93 }
94 }
95 }
96
97 impl TryFrom<Repr> for $t {
98 type Error = ConversionError;
99 #[inline]
100 fn try_from(value: Repr) -> Result<Self, Self::Error> {
101 let int: IBig = value.try_into()?;
102 int.try_into()
103 }
104 }
105
106 forward_conversion_to_repr!($t => RBig);
107 forward_conversion_to_repr!($t => Relaxed);
108 )*};
109}
110impl_conversion_for_prim_ints!(u8 u16 u32 u64 u128 usize i8 i16 i32 i64 i128 isize);
111
112macro_rules! impl_conversion_from_float {
113 ($t:ty) => {
114 impl TryFrom<$t> for Repr {
115 type Error = ConversionError;
116
117 fn try_from(value: $t) -> Result<Self, Self::Error> {
118 if value == 0. {
120 return Ok(Repr::zero());
121 }
122
123 match value.decode() {
124 Ok((man, exp)) => {
125 let repr = if exp >= 0 {
128 Repr {
129 numerator: IBig::from(man) << exp as usize,
130 denominator: UBig::ONE,
131 }
132 } else {
133 let mut denominator = UBig::ZERO;
134 denominator.set_bit((-exp) as _);
135 Repr {
136 numerator: IBig::from(man),
137 denominator,
138 }
139 };
140 Ok(repr)
141 }
142 Err(_) => Err(ConversionError::OutOfBounds),
143 }
144 }
145 }
146
147 impl TryFrom<$t> for RBig {
148 type Error = ConversionError;
149 #[inline]
150 fn try_from(value: $t) -> Result<Self, Self::Error> {
151 Repr::try_from(value).map(|repr| RBig(repr.reduce2()))
152 }
153 }
154 impl TryFrom<$t> for Relaxed {
155 type Error = ConversionError;
156 #[inline]
157 fn try_from(value: $t) -> Result<Self, Self::Error> {
158 Repr::try_from(value).map(|repr| Relaxed(repr.reduce2()))
159 }
160 }
161 };
162}
163impl_conversion_from_float!(f32);
164impl_conversion_from_float!(f64);
165
166macro_rules! impl_conversion_to_float {
167 ($t:ty [$lb:literal, $ub:literal]) => {
168 impl TryFrom<RBig> for $t {
169 type Error = ConversionError;
170
171 fn try_from(value: RBig) -> Result<Self, Self::Error> {
174 if value.0.numerator.is_zero() {
175 Ok(0.)
176 } else if value.0.denominator.is_power_of_two() {
177 let num_bits = value.0.numerator.bit_len();
179 let den_bits = value.0.denominator.trailing_zeros().unwrap();
180 let top_bit = num_bits as isize - den_bits as isize;
181 if top_bit > $ub {
182 Err(ConversionError::OutOfBounds)
184 } else if top_bit < $lb {
185 Err(ConversionError::LossOfPrecision)
186 } else {
187 match <$t>::encode(
188 value.0.numerator.try_into().unwrap(),
189 -(den_bits as i16),
190 ) {
191 Exact(v) => Ok(v),
192 Inexact(v, _) => {
193 if v.is_infinite() {
194 Err(ConversionError::OutOfBounds)
195 } else {
196 Err(ConversionError::LossOfPrecision)
197 }
198 }
199 }
200 }
201 } else {
202 Err(ConversionError::LossOfPrecision)
203 }
204 }
205 }
206
207 impl TryFrom<Relaxed> for $t {
208 type Error = ConversionError;
209
210 #[inline]
211 fn try_from(value: Relaxed) -> Result<Self, Self::Error> {
212 <$t>::try_from(value.canonicalize())
214 }
215 }
216 };
217}
218impl_conversion_to_float!(f32 [-149, 128]); impl_conversion_to_float!(f64 [-1074, 1024]); fn log2_floor_abs(numerator: &UBig, denominator: &UBig, exp: isize) -> isize {
227 let ge_power = if exp >= 0 {
228 numerator >= &(denominator << exp as usize)
229 } else {
230 &(numerator << (-exp) as usize) >= denominator
231 };
232 if ge_power {
233 exp
234 } else {
235 exp - 1
236 }
237}
238
239fn rounded_abs_mantissa(
243 numerator: UBig,
244 denominator: &UBig,
245 sign: Sign,
246 shift: isize,
247) -> Approximation<UBig, Sign> {
248 let (num, den) = if shift >= 0 {
249 (numerator, denominator << shift as usize)
250 } else {
251 (numerator << (-shift) as usize, denominator.clone())
252 };
253 let (man, r) = num.div_rem(&den);
254
255 if r.is_zero() {
256 Exact(man)
257 } else {
258 let half = (r << 1).cmp(&den);
259 if half == Ordering::Greater || (half == Ordering::Equal && man.bit(0)) {
260 Inexact(man + UBig::ONE, sign)
261 } else {
262 Inexact(man, -sign)
263 }
264 }
265}
266
267impl Repr {
268 fn to_f32_fast(&self) -> f32 {
270 if self.numerator.is_zero() {
272 return 0.;
273 }
274
275 let sign = self.numerator.sign();
277 let num_bits = self.numerator.bit_len();
278 let den_bits = self.denominator.bit_len();
279
280 let num_shift = num_bits as isize - 48;
281 let num48: i64 = if num_shift >= 0 {
282 (&self.numerator) >> num_shift as usize
283 } else {
284 (&self.numerator) << (-num_shift) as usize
285 }
286 .try_into()
287 .unwrap();
288
289 let den_shift = den_bits as isize - 24;
290 let den24: u32 = if den_shift >= 0 {
291 (&self.denominator) >> den_shift as usize
292 } else {
293 (&self.denominator) << (-den_shift) as usize
294 }
295 .try_into()
296 .unwrap();
297
298 let exponent = num_shift - den_shift;
300 if exponent >= 128 {
301 sign * f32::INFINITY
303 } else if exponent < -149 - 25 {
304 sign * 0f32
306 } else {
307 let (mut man, r) = num48.unsigned_abs().div_rem(den24 as u64);
308
309 let half = (r as u32 * 2).cmp(&den24);
311 if half == Ordering::Greater || (half == Ordering::Equal && man & 1 > 0) {
312 man += 1;
313 }
314 f32::encode(sign * man as i32, exponent as i16).value()
315 }
316 }
317
318 fn to_f64_fast(&self) -> f64 {
319 if self.numerator.is_zero() {
321 return 0.;
322 }
323
324 let sign = self.numerator.sign();
326 let num_bits = self.numerator.bit_len();
327 let den_bits = self.denominator.bit_len();
328
329 let num_shift = num_bits as isize - 106;
330 let num106: i128 = if num_shift >= 0 {
331 (&self.numerator) >> num_shift as usize
332 } else {
333 (&self.numerator) << (-num_shift) as usize
334 }
335 .try_into()
336 .unwrap();
337
338 let den_shift = den_bits as isize - 53;
339 let den53: u64 = if den_shift >= 0 {
340 (&self.denominator) >> den_shift as usize
341 } else {
342 (&self.denominator) << (-den_shift) as usize
343 }
344 .try_into()
345 .unwrap();
346
347 let exponent = num_shift - den_shift;
349 if exponent >= 1024 {
350 sign * f64::INFINITY
352 } else if exponent < -1074 - 54 {
353 sign * 0f64
355 } else {
356 let (mut man, r) = num106.unsigned_abs().div_rem(den53 as u128);
357
358 let half = (r as u64 * 2).cmp(&den53);
360 if half == Ordering::Greater || (half == Ordering::Equal && man & 1 > 0) {
361 man += 1;
362 }
363 f64::encode(sign * man as i64, exponent as i16).value()
364 }
365 }
366
367 fn to_f32(&self) -> Approximation<f32, Sign> {
369 if self.numerator.is_zero() {
371 return Exact(0.);
372 }
373
374 let sign = self.numerator.sign();
375 let numerator = (&self.numerator).unsigned_abs();
376 let exp = numerator.bit_len() as isize - self.denominator.bit_len() as isize;
381 if exp >= 129 {
382 return Inexact(sign * f32::INFINITY, sign);
384 } else if exp <= -151 {
385 return Inexact(sign * 0f32, -sign);
387 }
388
389 let top_exp = log2_floor_abs(&numerator, &self.denominator, exp);
390 if top_exp >= 128 {
391 Inexact(sign * f32::INFINITY, sign)
393 } else if top_exp < -150 {
394 Inexact(sign * 0f32, -sign)
396 } else {
397 let shift = (top_exp - 23).max(-149);
401 rounded_abs_mantissa(numerator, &self.denominator, sign, shift).and_then(|man| {
402 let man: u32 = man.try_into().unwrap();
403 if man == 0 {
404 Exact(sign * 0f32)
406 } else {
407 f32::encode(sign * man as i32, shift as i16)
408 }
409 })
410 }
411 }
412
413 fn to_f64(&self) -> Approximation<f64, Sign> {
414 if self.numerator.is_zero() {
416 return Exact(0.);
417 }
418
419 let sign = self.numerator.sign();
420 let numerator = (&self.numerator).unsigned_abs();
421 let exp = numerator.bit_len() as isize - self.denominator.bit_len() as isize;
423 if exp >= 1025 {
424 return Inexact(sign * f64::INFINITY, sign);
426 } else if exp <= -1076 {
427 return Inexact(sign * 0f64, -sign);
429 }
430
431 let top_exp = log2_floor_abs(&numerator, &self.denominator, exp);
432 if top_exp >= 1024 {
433 Inexact(sign * f64::INFINITY, sign)
435 } else if top_exp < -1075 {
436 Inexact(sign * 0f64, -sign)
438 } else {
439 let shift = (top_exp - 52).max(-1074);
441 rounded_abs_mantissa(numerator, &self.denominator, sign, shift).and_then(|man| {
442 let man: u64 = man.try_into().unwrap();
443 if man == 0 {
444 Exact(sign * 0f64)
445 } else {
446 f64::encode(sign * man as i64, shift as i16)
447 }
448 })
449 }
450 }
451}
452
453impl RBig {
454 #[inline]
473 pub fn to_f32_fast(&self) -> f32 {
474 self.0.to_f32_fast()
475 }
476
477 #[inline]
496 pub fn to_f64_fast(&self) -> f64 {
497 self.0.to_f64_fast()
498 }
499
500 #[inline]
521 pub fn to_f32(&self) -> Approximation<f32, Sign> {
522 self.0.to_f32()
523 }
524
525 #[inline]
546 pub fn to_f64(&self) -> Approximation<f64, Sign> {
547 self.0.to_f64()
548 }
549
550 #[inline]
570 pub fn to_int(&self) -> Approximation<IBig, Self> {
571 let (trunc, fract) = self.clone().split_at_point();
572 if fract.is_zero() {
573 Approximation::Exact(trunc)
574 } else {
575 Approximation::Inexact(trunc, fract)
576 }
577 }
578}
579
580impl Relaxed {
581 #[inline]
585 pub fn to_f32_fast(&self) -> f32 {
586 self.0.to_f32_fast()
587 }
588 #[inline]
592 pub fn to_f64_fast(&self) -> f64 {
593 self.0.to_f64_fast()
594 }
595
596 #[inline]
600 pub fn to_f32(&self) -> Approximation<f32, Sign> {
601 self.0.to_f32()
602 }
603 #[inline]
607 pub fn to_f64(&self) -> Approximation<f64, Sign> {
608 self.0.to_f64()
609 }
610 #[inline]
614 pub fn to_int(&self) -> Approximation<IBig, Self> {
615 let (trunc, fract) = self.clone().split_at_point();
616 if fract.is_zero() {
617 Approximation::Exact(trunc)
618 } else {
619 Approximation::Inexact(trunc, fract)
620 }
621 }
622}
623
624#[cfg(test)]
625mod tests {
626 use super::*;
627 use dashu_base::Sign::{Negative, Positive};
628
629 #[test]
630 fn test_ubig_try_from_repr() {
631 assert_eq!(UBig::try_from(RBig::from_parts(5.into(), UBig::ONE)), Ok(UBig::from(5u8)));
633 assert_eq!(UBig::try_from(RBig::ZERO), Ok(UBig::ZERO));
634 assert_eq!(
636 UBig::try_from(RBig::from_parts(1.into(), 2u8.into())),
637 Err(ConversionError::LossOfPrecision)
638 );
639 assert_eq!(
641 UBig::try_from(RBig::from_parts((-3).into(), UBig::ONE)),
642 Err(ConversionError::OutOfBounds)
643 );
644 }
645
646 #[test]
647 fn test_rbig_to_f64_without_double_rounding() {
648 let input =
649 RBig::from_parts((-10534148920556696739i128).into(), 73786976294838206464u128.into());
650
651 assert_eq!(input.to_f64(), Inexact(f64::from_bits(0xbfc2_461a_1430_9b17), Negative));
652 assert_eq!((-input).to_f64(), Inexact(f64::from_bits(0x3fc2_461a_1430_9b17), Positive));
653 }
654
655 #[test]
656 fn test_rbig_to_float_midpoints_tie_to_even() {
657 assert_eq!(
658 RBig::from_parts(((1u64 << 24) + 1).into(), (1u64 << 24).into()).to_f32(),
659 Inexact(1.0, Negative)
660 );
661 assert_eq!(
662 RBig::from_parts(((1u64 << 24) + 3).into(), (1u64 << 24).into()).to_f32(),
663 Inexact(f32::from_bits(0x3f80_0002), Positive)
664 );
665
666 assert_eq!(
667 RBig::from_parts(((1u64 << 53) + 1).into(), (1u64 << 53).into()).to_f64(),
668 Inexact(1.0, Negative)
669 );
670 assert_eq!(
671 RBig::from_parts(((1u64 << 53) + 3).into(), (1u64 << 53).into()).to_f64(),
672 Inexact(f64::from_bits(0x3ff0_0000_0000_0002), Positive)
673 );
674 }
675
676 #[test]
677 fn test_rbig_to_float_around_midpoints() {
678 assert_eq!(
679 RBig::from_parts(((1u64 << 25) + 1).into(), (1u64 << 25).into()).to_f32(),
680 Inexact(1.0, Negative)
681 );
682 assert_eq!(
683 RBig::from_parts(((1u64 << 25) + 3).into(), (1u64 << 25).into()).to_f32(),
684 Inexact(f32::from_bits(0x3f80_0001), Positive)
685 );
686
687 assert_eq!(
688 RBig::from_parts(((1u128 << 54) + 1).into(), (1u128 << 54).into()).to_f64(),
689 Inexact(1.0, Negative)
690 );
691 assert_eq!(
692 RBig::from_parts(((1u128 << 54) + 3).into(), (1u128 << 54).into()).to_f64(),
693 Inexact(f64::from_bits(0x3ff0_0000_0000_0001), Positive)
694 );
695 }
696
697 #[test]
698 fn test_rbig_to_float_subnormal_rounding_boundaries() {
699 assert_eq!(RBig::from_parts(1.into(), UBig::ONE << 150).to_f32(), Inexact(0.0, Negative));
700 assert_eq!(
701 RBig::from_parts((-1).into(), UBig::ONE << 150).to_f32(),
702 Inexact(-0.0, Positive)
703 );
704 assert_eq!(
705 RBig::from_parts(3.into(), UBig::ONE << 151).to_f32(),
706 Inexact(f32::from_bits(1), Positive)
707 );
708 assert_eq!(
709 RBig::from_parts(3.into(), UBig::ONE << 150).to_f32(),
710 Inexact(f32::from_bits(2), Positive)
711 );
712
713 assert_eq!(RBig::from_parts(1.into(), UBig::ONE << 1075).to_f64(), Inexact(0.0, Negative));
714 assert_eq!(
715 RBig::from_parts((-1).into(), UBig::ONE << 1075).to_f64(),
716 Inexact(-0.0, Positive)
717 );
718 assert_eq!(
719 RBig::from_parts(3.into(), UBig::ONE << 1076).to_f64(),
720 Inexact(f64::from_bits(1), Positive)
721 );
722 assert_eq!(
723 RBig::from_parts(3.into(), UBig::ONE << 1075).to_f64(),
724 Inexact(f64::from_bits(2), Positive)
725 );
726 }
727}