use super::float::ExtendedFloat;
use super::num::*;
use super::shift::*;
use core::mem;
#[inline]
pub(crate) fn nth_bit(n: u64) -> u64 {
let bits: u64 = mem::size_of::<u64>() as u64 * 8;
debug_assert!(n < bits, "nth_bit() overflow in shl.");
1 << n
}
#[inline]
pub(crate) fn lower_n_mask(n: u64) -> u64 {
let bits: u64 = mem::size_of::<u64>() as u64 * 8;
debug_assert!(n <= bits, "lower_n_mask() overflow in shl.");
if n == bits {
u64::max_value()
} else {
(1 << n) - 1
}
}
#[inline]
pub(crate) fn lower_n_halfway(n: u64) -> u64 {
let bits: u64 = mem::size_of::<u64>() as u64 * 8;
debug_assert!(n <= bits, "lower_n_halfway() overflow in shl.");
if n == 0 {
0
} else {
nth_bit(n - 1)
}
}
#[inline]
pub(crate) fn internal_n_mask(bit: u64, n: u64) -> u64 {
let bits: u64 = mem::size_of::<u64>() as u64 * 8;
debug_assert!(bit <= bits, "internal_n_halfway() overflow in shl.");
debug_assert!(n <= bits, "internal_n_halfway() overflow in shl.");
debug_assert!(bit >= n, "internal_n_halfway() overflow in sub.");
lower_n_mask(bit) ^ lower_n_mask(bit - n)
}
#[inline]
pub(crate) fn round_nearest(fp: &mut ExtendedFloat, shift: i32) -> (bool, bool) {
let mask: u64 = lower_n_mask(shift as u64);
let halfway: u64 = lower_n_halfway(shift as u64);
let truncated_bits = fp.mant & mask;
let is_above = truncated_bits > halfway;
let is_halfway = truncated_bits == halfway;
overflowing_shr(fp, shift);
(is_above, is_halfway)
}
#[inline]
pub(crate) fn tie_even(fp: &mut ExtendedFloat, is_above: bool, is_halfway: bool) {
let is_odd = fp.mant & 1 == 1;
if is_above || (is_odd && is_halfway) {
fp.mant += 1;
}
}
#[inline]
pub(crate) fn round_nearest_tie_even(fp: &mut ExtendedFloat, shift: i32) {
let (is_above, is_halfway) = round_nearest(fp, shift);
tie_even(fp, is_above, is_halfway);
}
#[inline]
fn round_toward(fp: &mut ExtendedFloat, shift: i32) -> bool {
let mask: u64 = lower_n_mask(shift as u64);
let truncated_bits = fp.mant & mask;
overflowing_shr(fp, shift);
truncated_bits != 0
}
#[inline]
fn downard(_: &mut ExtendedFloat, _: bool) {}
#[inline]
pub(crate) fn round_downward(fp: &mut ExtendedFloat, shift: i32) {
let is_truncated = round_toward(fp, shift);
downard(fp, is_truncated);
}
#[inline]
pub(crate) fn round_to_float<F, Algorithm>(fp: &mut ExtendedFloat, algorithm: Algorithm)
where
F: Float,
Algorithm: FnOnce(&mut ExtendedFloat, i32),
{
let final_exp = fp.exp + F::DEFAULT_SHIFT;
if final_exp < F::DENORMAL_EXPONENT {
let diff = F::DENORMAL_EXPONENT - fp.exp;
if diff <= u64::FULL {
algorithm(fp, diff);
} else {
fp.mant = 0;
fp.exp = 0;
}
} else {
algorithm(fp, F::DEFAULT_SHIFT);
}
if fp.mant & F::CARRY_MASK == F::CARRY_MASK {
shr(fp, 1);
}
}
#[inline]
pub(crate) fn avoid_overflow<F>(fp: &mut ExtendedFloat)
where
F: Float,
{
if fp.exp >= F::MAX_EXPONENT {
let diff = fp.exp - F::MAX_EXPONENT;
if diff <= F::MANTISSA_SIZE {
let bit = (F::MANTISSA_SIZE + 1) as u64;
let n = (diff + 1) as u64;
let mask = internal_n_mask(bit, n);
if (fp.mant & mask) == 0 {
let shift = diff + 1;
shl(fp, shift);
}
}
}
}
#[inline]
pub(crate) fn round_to_native<F, Algorithm>(fp: &mut ExtendedFloat, algorithm: Algorithm)
where
F: Float,
Algorithm: FnOnce(&mut ExtendedFloat, i32),
{
fp.normalize();
round_to_float::<F, _>(fp, algorithm);
avoid_overflow::<F>(fp);
}