use crate::env::{ExcFlags, FloatEnv};
use crate::parts::{
nan_propagate, return_nan, round_pack, unpack, FloatClass, FloatParts,
};
use crate::types::{
BFloat16, Float128, Float16, Float32, Float64, FloatFormat, FloatX80,
};
pub fn div<F: FloatFormat>(a: F, b: F, env: &mut FloatEnv) -> F {
let pa = unpack::<F>(a);
let pb = unpack::<F>(b);
div_parts::<F>(&pa, &pb, env)
}
fn div_parts<F: FloatFormat>(
a: &FloatParts,
b: &FloatParts,
env: &mut FloatEnv,
) -> F {
let result_sign = a.sign ^ b.sign;
if a.is_nan() || b.is_nan() {
let mut r = nan_propagate(a, b, env);
r.sign = result_sign;
return round_pack::<F>(&mut r, env);
}
if a.cls == FloatClass::Inf {
if b.cls == FloatClass::Inf {
return return_nan::<F>(env);
}
let mut r = FloatParts {
sign: result_sign,
exp: 0,
frac: 0,
cls: FloatClass::Inf,
};
return round_pack::<F>(&mut r, env);
}
if b.cls == FloatClass::Inf {
let mut r = FloatParts {
sign: result_sign,
exp: 0,
frac: 0,
cls: FloatClass::Zero,
};
return round_pack::<F>(&mut r, env);
}
if a.cls == FloatClass::Zero {
if b.cls == FloatClass::Zero {
return return_nan::<F>(env);
}
let mut r = FloatParts {
sign: result_sign,
exp: 0,
frac: 0,
cls: FloatClass::Zero,
};
return round_pack::<F>(&mut r, env);
}
if b.cls == FloatClass::Zero {
env.raise(ExcFlags::DIVBYZERO);
let mut r = FloatParts {
sign: result_sign,
exp: 0,
frac: 0,
cls: FloatClass::Inf,
};
return round_pack::<F>(&mut r, env);
}
let exp = a.exp - b.exp;
let (frac, exp) = div_frac(a.frac, b.frac, exp);
let mut result = FloatParts {
sign: result_sign,
exp,
frac,
cls: FloatClass::Normal,
};
round_pack::<F>(&mut result, env)
}
fn div_frac(a_frac: u128, b_frac: u128, mut exp: i32) -> (u128, i32) {
if b_frac == 0 {
return (0, exp);
}
let mut rem = a_frac;
if rem < b_frac {
rem <<= 1;
exp -= 1;
}
let mut q: u128 = 0;
for _ in 0..128 {
q <<= 1;
if rem >= b_frac {
q |= 1;
rem -= b_frac;
}
rem <<= 1;
}
if rem != 0 {
q |= 1;
}
let sticky = q & 1;
let frac = (q >> 1) | sticky;
(frac, exp)
}
macro_rules! impl_div {
($ty:ty) => {
impl $ty {
pub fn div(self, other: Self, env: &mut FloatEnv) -> Self {
div::<Self>(self, other, env)
}
}
};
}
impl_div!(Float16);
impl_div!(BFloat16);
impl_div!(Float32);
impl_div!(Float64);
impl_div!(Float128);
impl_div!(FloatX80);