use crate::env::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 mul<F: FloatFormat>(a: F, b: F, env: &mut FloatEnv) -> F {
let pa = unpack::<F>(a);
let pb = unpack::<F>(b);
mul_parts::<F>(&pa, &pb, env)
}
fn mul_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::Zero {
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 {
if a.cls == FloatClass::Zero {
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 a.cls == FloatClass::Zero || b.cls == FloatClass::Zero {
let mut r = FloatParts {
sign: result_sign,
exp: 0,
frac: 0,
cls: FloatClass::Zero,
};
return round_pack::<F>(&mut r, env);
}
let exp = a.exp + b.exp;
let (hi, lo) = mul_u128(a.frac, b.frac);
let frac = (hi << 2) | (lo >> 126);
let sticky = if lo & ((1u128 << 126) - 1) != 0 { 1 } else { 0 };
let frac = frac | sticky;
let mut result = FloatParts {
sign: result_sign,
exp,
frac,
cls: FloatClass::Normal,
};
round_pack::<F>(&mut result, env)
}
fn mul_u128(a: u128, b: u128) -> (u128, u128) {
let a_lo = a as u64 as u128;
let a_hi = (a >> 64) as u64 as u128;
let b_lo = b as u64 as u128;
let b_hi = (b >> 64) as u64 as u128;
let ll = a_lo * b_lo;
let lh = a_lo * b_hi;
let hl = a_hi * b_lo;
let hh = a_hi * b_hi;
let mid = (ll >> 64)
+ (lh & 0xFFFF_FFFF_FFFF_FFFF)
+ (hl & 0xFFFF_FFFF_FFFF_FFFF);
let lo =
(ll & 0xFFFF_FFFF_FFFF_FFFF) | ((mid & 0xFFFF_FFFF_FFFF_FFFF) << 64);
let hi = hh + (lh >> 64) + (hl >> 64) + (mid >> 64);
(hi, lo)
}
macro_rules! impl_mul {
($ty:ty) => {
impl $ty {
pub fn mul(self, other: Self, env: &mut FloatEnv) -> Self {
mul::<Self>(self, other, env)
}
}
};
}
impl_mul!(Float16);
impl_mul!(BFloat16);
impl_mul!(Float32);
impl_mul!(Float64);
impl_mul!(Float128);
impl_mul!(FloatX80);