use crate::lib::mem;
use super::float::ExtendedFloat;
use super::num::*;
use super::shift::*;
#[inline]
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.");
match n == bits {
true => u64::max_value(),
false => (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.");
match n == 0 {
true => 0,
false => nth_bit(n - 1),
}
}
#[inline]
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]
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]
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);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn lower_n_mask_test() {
assert_eq!(lower_n_mask(0u64), 0b0);
assert_eq!(lower_n_mask(1u64), 0b1);
assert_eq!(lower_n_mask(2u64), 0b11);
assert_eq!(lower_n_mask(10u64), 0b1111111111);
assert_eq!(lower_n_mask(32u64), 0b11111111111111111111111111111111);
}
#[test]
fn lower_n_halfway_test() {
assert_eq!(lower_n_halfway(0u64), 0b0);
assert_eq!(lower_n_halfway(1u64), 0b1);
assert_eq!(lower_n_halfway(2u64), 0b10);
assert_eq!(lower_n_halfway(10u64), 0b1000000000);
assert_eq!(lower_n_halfway(32u64), 0b10000000000000000000000000000000);
}
#[test]
fn nth_bit_test() {
assert_eq!(nth_bit(0u64), 0b1);
assert_eq!(nth_bit(1u64), 0b10);
assert_eq!(nth_bit(2u64), 0b100);
assert_eq!(nth_bit(10u64), 0b10000000000);
assert_eq!(nth_bit(31u64), 0b10000000000000000000000000000000);
}
#[test]
fn internal_n_mask_test() {
assert_eq!(internal_n_mask(1u64, 0u64), 0b0);
assert_eq!(internal_n_mask(1u64, 1u64), 0b1);
assert_eq!(internal_n_mask(2u64, 1u64), 0b10);
assert_eq!(internal_n_mask(4u64, 2u64), 0b1100);
assert_eq!(internal_n_mask(10u64, 2u64), 0b1100000000);
assert_eq!(internal_n_mask(10u64, 4u64), 0b1111000000);
assert_eq!(internal_n_mask(32u64, 4u64), 0b11110000000000000000000000000000);
}
#[test]
fn round_nearest_test() {
let mut fp = ExtendedFloat { mant: 0x60, exp: 0 };
let (above, halfway) = round_nearest(&mut fp, 6);
assert!(!above);
assert!(halfway);
assert_eq!(fp.mant, 1);
let mut fp = ExtendedFloat { mant: 0x61, exp: 0 };
let (above, halfway) = round_nearest(&mut fp, 6);
assert!(above);
assert!(!halfway);
assert_eq!(fp.mant, 1);
let mut fp = ExtendedFloat { mant: 0x5F, exp: 0 };
let (above, halfway) = round_nearest(&mut fp, 6);
assert!(!above);
assert!(!halfway);
assert_eq!(fp.mant, 1);
}
#[test]
fn round_downward_test() {
let mut fp = ExtendedFloat { mant: 0x00, exp: 0 };
round_downward(&mut fp, 6);
assert_eq!(fp.mant, 0);
let mut fp = ExtendedFloat { mant: 0x40, exp: 0 };
round_downward(&mut fp, 6);
assert_eq!(fp.mant, 1);
let mut fp = ExtendedFloat { mant: 0x60, exp: 0 };
round_downward(&mut fp, 6);
assert_eq!(fp.mant, 1);
let mut fp = ExtendedFloat { mant: 0x70, exp: 0 };
round_downward(&mut fp, 6);
assert_eq!(fp.mant, 1);
}
#[test]
fn round_nearest_tie_even_test() {
let mut fp = ExtendedFloat { mant: 0x60, exp: 0 };
round_nearest_tie_even(&mut fp, 6);
assert_eq!(fp.mant, 2);
let mut fp = ExtendedFloat { mant: 0x20, exp: 0 };
round_nearest_tie_even(&mut fp, 6);
assert_eq!(fp.mant, 0);
let mut fp = ExtendedFloat { mant: 0x61, exp: 0 };
round_nearest_tie_even(&mut fp, 6);
assert_eq!(fp.mant, 2);
let mut fp = ExtendedFloat { mant: 0x21, exp: 0 };
round_nearest_tie_even(&mut fp, 6);
assert_eq!(fp.mant, 1);
let mut fp = ExtendedFloat { mant: 0x5F, exp: 0 };
round_nearest_tie_even(&mut fp, 6);
assert_eq!(fp.mant, 1);
let mut fp = ExtendedFloat { mant: 0x1F, exp: 0 };
round_nearest_tie_even(&mut fp, 6);
assert_eq!(fp.mant, 0);
}
#[test]
fn round_to_float_test() {
let mut fp = ExtendedFloat { mant: 1<<63, exp: f64::DENORMAL_EXPONENT - 15 };
round_to_float::<f64, _>(&mut fp, round_nearest_tie_even);
assert_eq!(fp.mant, 1<<48);
assert_eq!(fp.exp, f64::DENORMAL_EXPONENT);
let mut fp = ExtendedFloat { mant: 0x8000000000000400, exp: -63 };
round_to_float::<f64, _>(&mut fp, round_nearest_tie_even);
assert_eq!(fp.mant, 1<<52);
assert_eq!(fp.exp, -52);
let mut fp = ExtendedFloat { mant: 0x8000000000000C00, exp: -63 };
round_to_float::<f64, _>(&mut fp, round_nearest_tie_even);
assert_eq!(fp.mant, (1<<52) + 2);
assert_eq!(fp.exp, -52);
let mut fp = ExtendedFloat { mant: 0x8000000000000401, exp: -63 };
round_to_float::<f64, _>(&mut fp, round_nearest_tie_even);
assert_eq!(fp.mant, (1<<52)+1);
assert_eq!(fp.exp, -52);
let mut fp = ExtendedFloat { mant: 0x8000000000000C01, exp: -63 };
round_to_float::<f64, _>(&mut fp, round_nearest_tie_even);
assert_eq!(fp.mant, (1<<52) + 2);
assert_eq!(fp.exp, -52);
let mut fp = ExtendedFloat { mant: 0x80000000000003FF, exp: -63 };
round_to_float::<f64, _>(&mut fp, round_nearest_tie_even);
assert_eq!(fp.mant, 1<<52);
assert_eq!(fp.exp, -52);
let mut fp = ExtendedFloat { mant: 0x8000000000000BFF, exp: -63 };
round_to_float::<f64, _>(&mut fp, round_nearest_tie_even);
assert_eq!(fp.mant, (1<<52) + 1);
assert_eq!(fp.exp, -52);
}
#[test]
fn avoid_overflow_test() {
let mut fp = ExtendedFloat { mant: 0xFFFFFFFFFFFF, exp: f64::MAX_EXPONENT + 5 };
avoid_overflow::<f64>(&mut fp);
assert_eq!(fp.mant, 0xFFFFFFFFFFFF);
assert_eq!(fp.exp, f64::MAX_EXPONENT+5);
let mut fp = ExtendedFloat { mant: 0xFFFFFFFFFFFF, exp: f64::MAX_EXPONENT + 4 };
avoid_overflow::<f64>(&mut fp);
assert_eq!(fp.mant, 0x1FFFFFFFFFFFE0);
assert_eq!(fp.exp, f64::MAX_EXPONENT-1);
}
#[test]
fn round_to_native_test() {
let mut fp = ExtendedFloat { mant: 0xFFFFFFFFFFFF, exp: f64::MAX_EXPONENT + 4 };
round_to_native::<f64, _>(&mut fp, round_nearest_tie_even);
assert_eq!(fp.mant, 0x1FFFFFFFFFFFE0);
assert_eq!(fp.exp, f64::MAX_EXPONENT-1);
let mut fp = ExtendedFloat { mant: 1, exp: f64::DENORMAL_EXPONENT +48 };
round_to_native::<f64, _>(&mut fp, round_nearest_tie_even);
assert_eq!(fp.mant, 1<<48);
assert_eq!(fp.exp, f64::DENORMAL_EXPONENT);
let mut fp = ExtendedFloat { mant: 0x400000000000020, exp: -58 };
round_to_native::<f64, _>(&mut fp, round_nearest_tie_even);
assert_eq!(fp.mant, 1<<52);
assert_eq!(fp.exp, -52);
let mut fp = ExtendedFloat { mant: 0x400000000000060, exp: -58 };
round_to_native::<f64, _>(&mut fp, round_nearest_tie_even);
assert_eq!(fp.mant, (1<<52) + 2);
assert_eq!(fp.exp, -52);
let mut fp = ExtendedFloat { mant: 0x400000000000021, exp: -58 };
round_to_native::<f64, _>(&mut fp, round_nearest_tie_even);
assert_eq!(fp.mant, (1<<52)+1);
assert_eq!(fp.exp, -52);
let mut fp = ExtendedFloat { mant: 0x400000000000061, exp: -58 };
round_to_native::<f64, _>(&mut fp, round_nearest_tie_even);
assert_eq!(fp.mant, (1<<52) + 2);
assert_eq!(fp.exp, -52);
let mut fp = ExtendedFloat { mant: 0x40000000000001F, exp: -58 };
round_to_native::<f64, _>(&mut fp, round_nearest_tie_even);
assert_eq!(fp.mant, 1<<52);
assert_eq!(fp.exp, -52);
let mut fp = ExtendedFloat { mant: 0x40000000000005F, exp: -58 };
round_to_native::<f64, _>(&mut fp, round_nearest_tie_even);
assert_eq!(fp.mant, (1<<52) + 1);
assert_eq!(fp.exp, -52);
let mut fp = ExtendedFloat { exp: -1139, mant: 18446744073709550712 };
round_to_native::<f64, _>(&mut fp, round_nearest_tie_even);
assert_eq!(fp.mant, 0);
assert_eq!(fp.exp, 0);
let mut fp = ExtendedFloat { exp: -1139, mant: 18446744073709551460 };
round_to_native::<f64, _>(&mut fp, round_nearest_tie_even);
assert_eq!(fp.mant, 0);
assert_eq!(fp.exp, 0);
let mut fp = ExtendedFloat { exp: -1138, mant: 9223372036854776103 };
round_to_native::<f64, _>(&mut fp, round_nearest_tie_even);
assert_eq!(fp.mant, 1);
assert_eq!(fp.exp, -1074);
}
}