use crate::{
bint::UInt,
decimal::{
dec::{convert::to_float::float, math::mul::mul},
Decimal, RoundingMode, Sign,
},
};
type D<const N: usize> = Decimal<N>;
type U<const N: usize> = UInt<N>;
#[inline]
const fn decimal_round<const N: usize>(d: D<N>) -> u64 {
let rounded = d.with_rounding_mode(RoundingMode::HalfEven).round(0);
let digits = rounded.resize().digits();
if digits.ge(&U::<1>::MAX) {
return u64::MAX;
}
digits.digits()[0]
}
#[inline]
const fn fast_mul_power_two<const N: usize>(d: &mut D<N>, mut power: u32) {
while power > 0 {
let shift = if power <= U::<N>::MAX_POWER_OF_TWO {
power
} else {
U::<N>::MAX_POWER_OF_TWO
};
*d = mul(
*d,
D::from_parts(U::power_of_two(shift), 0, Sign::Plus, d.context()),
);
power -= shift;
}
}
#[inline]
const fn fast_div_power_two<const N: usize>(d: &mut D<N>, mut power: u32) {
while power > 0 {
let shift = if power <= U::<N>::MAX_POWER_OF_FIVE {
power
} else {
U::<N>::MAX_POWER_OF_FIVE
};
*d = mul(
*d,
D::from_parts(
U::power_of_five(shift),
-(shift as i32),
Sign::Plus,
d.context(),
),
);
power -= shift;
}
}
macro_rules! to_float_slow_impl {
($to_f: ident, $f: ident) => {
#[inline]
pub const fn $to_f<const N: usize>(mut d: D<N>) -> $f {
use float::$f::*;
let mut exp2 = 0_i32;
while true {
let decimal_point = d.digits.ilog10() as i32 + 1 - d.cb.get_scale() as i32;
if decimal_point > INFINITE_POWER {
return $f::INFINITY;
} else if decimal_point < -INFINITE_POWER {
return 0.0;
}
if decimal_point <= 0 {
break;
}
let shift = decimal_point as u32;
fast_div_power_two(&mut d, shift);
exp2 += shift as i32;
}
while d.lt(&D::HALF) {
let decimal_point = d.digits.ilog10() as i32 + 1 - d.cb.get_scale() as i32;
debug_assert!(decimal_point <= 0);
if decimal_point > INFINITE_POWER {
return $f::INFINITY;
} else if decimal_point < -INFINITE_POWER {
return 0.0;
}
let shift = if decimal_point == 0 {
1
} else {
-decimal_point as u32
};
fast_mul_power_two(&mut d, shift);
exp2 -= shift as i32;
}
exp2 -= 1;
while EXP_MIN > exp2 {
fast_div_power_two(&mut d, 1);
exp2 += 1;
}
if (exp2 - EXP_MIN + 1) >= INFINITE_POWER {
return $f::INFINITY;
}
fast_mul_power_two(&mut d, SIG_BITS + 1);
let mut mantissa = decimal_round(d);
if ((mantissa >> (SIG_BITS + 1)) != 0) {
mantissa >>= 1;
exp2 += 1;
if ((exp2 - EXP_MIN + 1) >= INFINITE_POWER) {
return $f::INFINITY;
}
}
let mut power2 = exp2 - EXP_MIN + 1;
if mantissa < (1_u64 << SIG_BITS) {
power2 -= 1;
}
mantissa &= (1_u64 << SIG_BITS) - 1;
float(mantissa, power2)
}
};
}
to_float_slow_impl!(to_f64, f64);
to_float_slow_impl!(to_f32, f32);