use crate::env::FloatEnv;
use crate::parts::{
nan_propagate_one, return_nan, round_pack, unpack, FloatClass, FloatParts,
};
use crate::types::{
BFloat16, Float128, Float16, Float32, Float64, FloatFormat, FloatX80,
};
const INT_BIT: u32 = 126;
pub fn sqrt<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 {
if pa.sign {
return return_nan::<F>(env);
}
let mut r = pa;
return round_pack::<F>(&mut r, env);
}
if pa.cls == FloatClass::Zero {
let mut r = pa;
return round_pack::<F>(&mut r, env);
}
if pa.sign {
return return_nan::<F>(env);
}
let mut frac = pa.frac;
let mut exp = pa.exp;
if exp & 1 != 0 {
frac >>= 1;
exp += 1;
}
let result_exp = exp >> 1;
let result_frac = isqrt_u128(frac);
let mut result = FloatParts {
sign: false,
exp: result_exp,
frac: result_frac,
cls: FloatClass::Normal,
};
round_pack::<F>(&mut result, env)
}
fn isqrt_u128(frac: u128) -> u128 {
if frac == 0 {
return 0;
}
let mut rem: u128 = 0;
let mut result: u128 = 0;
for i in (0..=INT_BIT).rev() {
let bit_pos = i;
let rb_hi = 2 * bit_pos + 1;
let rb_lo = 2 * bit_pos;
let get_radicand_bit = |pos: u32| -> u128 {
if pos >= 253 {
return 0;
}
if pos >= 126 {
(frac >> (pos - 126)) & 1
} else {
0 }
};
rem = (rem << 2)
| (get_radicand_bit(rb_hi) << 1)
| get_radicand_bit(rb_lo);
let trial = (result << 2) | 1;
if rem >= trial {
rem -= trial;
result = (result << 1) | 1;
} else {
result <<= 1;
}
}
if rem != 0 {
result |= 1;
}
result
}
macro_rules! impl_sqrt {
($ty:ty) => {
impl $ty {
pub fn sqrt(self, env: &mut FloatEnv) -> Self {
sqrt::<Self>(self, env)
}
}
};
}
impl_sqrt!(Float16);
impl_sqrt!(BFloat16);
impl_sqrt!(Float32);
impl_sqrt!(Float64);
impl_sqrt!(Float128);
impl_sqrt!(FloatX80);