use crate::env::{ExcFlags, FloatEnv};
use crate::parts::{nan_propagate, round_pack, unpack, FloatClass};
use crate::types::{
BFloat16, Float128, Float16, Float32, Float64, FloatFormat, FloatX80,
};
pub fn min<F: FloatFormat>(a: F, b: F, env: &mut FloatEnv) -> F {
let pa = unpack::<F>(a);
let pb = unpack::<F>(b);
if pa.cls == FloatClass::SNaN || pb.cls == FloatClass::SNaN {
env.raise(ExcFlags::INVALID);
}
if pa.is_nan() && pb.is_nan() {
let mut r = nan_propagate(&pa, &pb, env);
return round_pack::<F>(&mut r, env);
}
if pa.is_nan() {
return b;
}
if pb.is_nan() {
return a;
}
if pa.cls == FloatClass::Zero && pb.cls == FloatClass::Zero {
return if pb.sign { b } else { a };
}
let a_lt_b = if pa.sign != pb.sign {
pa.sign } else if pa.sign {
if pa.exp != pb.exp {
pa.exp > pb.exp
} else {
pa.frac > pb.frac
}
} else {
if pa.exp != pb.exp {
pa.exp < pb.exp
} else {
pa.frac < pb.frac
}
};
if a_lt_b {
a
} else {
b
}
}
pub fn max<F: FloatFormat>(a: F, b: F, env: &mut FloatEnv) -> F {
let pa = unpack::<F>(a);
let pb = unpack::<F>(b);
if pa.cls == FloatClass::SNaN || pb.cls == FloatClass::SNaN {
env.raise(ExcFlags::INVALID);
}
if pa.is_nan() && pb.is_nan() {
let mut r = nan_propagate(&pa, &pb, env);
return round_pack::<F>(&mut r, env);
}
if pa.is_nan() {
return b;
}
if pb.is_nan() {
return a;
}
if pa.cls == FloatClass::Zero && pb.cls == FloatClass::Zero {
return if pa.sign { b } else { a };
}
let a_gt_b = if pa.sign != pb.sign {
pb.sign } else if pa.sign {
if pa.exp != pb.exp {
pa.exp < pb.exp
} else {
pa.frac < pb.frac
}
} else {
if pa.exp != pb.exp {
pa.exp > pb.exp
} else {
pa.frac > pb.frac
}
};
if a_gt_b {
a
} else {
b
}
}
macro_rules! impl_minmax {
($ty:ty) => {
impl $ty {
pub fn min(self, other: Self, env: &mut FloatEnv) -> Self {
min::<Self>(self, other, env)
}
pub fn max(self, other: Self, env: &mut FloatEnv) -> Self {
max::<Self>(self, other, env)
}
}
};
}
impl_minmax!(Float16);
impl_minmax!(BFloat16);
impl_minmax!(Float32);
impl_minmax!(Float64);
impl_minmax!(Float128);
impl_minmax!(FloatX80);