use crate::env::{ExcFlags, FloatEnv, RoundMode};
use crate::parts::{
nan_propagate_one, round_pack, unpack, FloatClass, FloatParts,
};
use crate::types::{
BFloat16, Float128, Float16, Float32, Float64, FloatFormat, FloatX80,
};
const INT_BIT: u32 = 126;
pub fn round_to_int<F: FloatFormat>(a: F, env: &mut FloatEnv) -> F {
let pa = unpack::<F>(a);
if pa.is_nan() {
let mut r = nan_propagate_one(&pa, env);
return round_pack::<F>(&mut r, env);
}
if pa.cls == FloatClass::Inf || pa.cls == FloatClass::Zero {
let mut r = pa;
return round_pack::<F>(&mut r, env);
}
if pa.exp >= F::FRAC_BITS as i32 {
let mut r = pa;
return round_pack::<F>(&mut r, env);
}
if pa.exp < 0 {
let rm = env.round_mode();
let inexact = pa.frac != 0;
if inexact {
env.raise(ExcFlags::INEXACT);
}
let round_up = match rm {
RoundMode::NearEven => {
pa.exp == -1 && pa.frac > (1u128 << INT_BIT)
}
RoundMode::NearMaxMag => pa.exp == -1,
RoundMode::ToZero => false,
RoundMode::Up => !pa.sign && inexact,
RoundMode::Down => pa.sign && inexact,
RoundMode::Odd => true,
};
if round_up {
let mut r = FloatParts {
sign: pa.sign,
exp: 0,
frac: 1u128 << INT_BIT,
cls: FloatClass::Normal,
};
return round_pack::<F>(&mut r, env);
}
let mut r = FloatParts {
sign: pa.sign,
exp: 0,
frac: 0,
cls: FloatClass::Zero,
};
return round_pack::<F>(&mut r, env);
}
let frac_shift = INT_BIT - pa.exp as u32;
let frac_mask = (1u128 << frac_shift) - 1;
let remainder = pa.frac & frac_mask;
if remainder == 0 {
let mut r = pa;
return round_pack::<F>(&mut r, env);
}
env.raise(ExcFlags::INEXACT);
let rm = env.round_mode();
let half = 1u128 << (frac_shift - 1);
let truncated = pa.frac & !frac_mask;
let lsb_set = (pa.frac >> frac_shift) & 1 != 0;
let increment = 1u128 << frac_shift;
let round_up = match rm {
RoundMode::NearEven => {
if remainder > half {
true
} else if remainder == half {
lsb_set
} else {
false
}
}
RoundMode::NearMaxMag => remainder >= half,
RoundMode::ToZero => false,
RoundMode::Up => !pa.sign,
RoundMode::Down => pa.sign,
RoundMode::Odd => {
!lsb_set
}
};
let frac = if round_up {
truncated.wrapping_add(increment)
} else {
truncated
};
let mut r = FloatParts {
sign: pa.sign,
exp: pa.exp,
frac,
cls: FloatClass::Normal,
};
round_pack::<F>(&mut r, env)
}
macro_rules! impl_round {
($ty:ty) => {
impl $ty {
pub fn round_to_int(self, env: &mut FloatEnv) -> Self {
round_to_int::<Self>(self, env)
}
}
};
}
impl_round!(Float16);
impl_round!(BFloat16);
impl_round!(Float32);
impl_round!(Float64);
impl_round!(Float128);
impl_round!(FloatX80);