use super::exp::exp_inner_common;
use super::{round_as_i_f, Exp};
use crate::traits::{Int as _, Like};
pub(crate) trait Exp2<L = Like<Self>>: Exp {
fn ln_2() -> Self;
fn exp2_lo_th() -> Self;
fn exp2_hi_th() -> Self;
}
pub(crate) fn exp2<F: Exp2>(x: F) -> F {
if x >= F::exp2_hi_th() {
F::INFINITY
} else if x <= F::exp2_lo_th() {
F::ZERO
} else {
let e = x.raw_exp();
if e == F::RawExp::ZERO {
F::one()
} else if e == F::MAX_RAW_EXP {
x
} else {
exp2_inner(x)
}
}
}
fn exp2_inner<F: Exp2>(x: F) -> F {
let (k, r_hi, r_lo) = exp2_split(x);
exp_inner_common(k, r_hi, r_lo)
}
#[inline]
fn exp2_split<F: Exp2>(x: F) -> (i32, F, F) {
let (k, kf) = round_as_i_f(x);
let t = x - kf;
let (t_hi, t_lo) = t.split_hi_lo();
let r_hi = t_hi * F::ln_2_hi();
let r_lo = t_hi * F::ln_2_lo() + t_lo * F::ln_2();
(k, r_hi, r_lo)
}
#[cfg(test)]
mod tests {
use crate::traits::Float;
use crate::FloatMath;
fn test<F: Float + FloatMath>(lo_th: &str, hi_th: &str) {
use crate::exp2;
let f = F::parse;
let lo_th = f(lo_th);
let hi_th = f(hi_th);
assert_is_nan!(exp2(F::NAN));
assert_total_eq!(exp2(F::INFINITY), F::INFINITY);
assert_total_eq!(exp2(F::neg_infinity()), F::ZERO);
assert_total_eq!(exp2(F::ZERO), F::one());
assert_total_eq!(exp2(-F::ZERO), F::one());
assert_total_eq!(exp2(F::one()), F::two());
assert_total_eq!(exp2(F::two()), f("4"));
assert_total_eq!(exp2(f("32")), f("4294967296"));
assert_total_eq!(exp2(-F::one()), F::half());
assert_total_eq!(exp2(-F::two()), f("0.25"));
assert_total_eq!(exp2(f("-3")), f("0.125"));
assert_total_eq!(exp2(f("-4")), f("0.0625"));
assert_total_eq!(exp2(lo_th), F::ZERO);
assert_total_eq!(exp2(lo_th - F::one()), F::ZERO);
assert_total_eq!(exp2(lo_th - F::two()), F::ZERO);
assert_total_eq!(exp2(hi_th), F::INFINITY);
assert_total_eq!(exp2(hi_th + F::one()), F::INFINITY);
assert_total_eq!(exp2(hi_th + F::two()), F::INFINITY);
}
#[test]
fn test_f32() {
test::<f32>("-150", "128");
}
#[cfg(feature = "soft-float")]
#[test]
fn test_soft_f32() {
test::<crate::SoftF32>("-150", "128");
}
#[test]
fn test_f64() {
test::<f64>("-1075", "1024");
}
#[cfg(feature = "soft-float")]
#[test]
fn test_soft_f64() {
test::<crate::SoftF64>("-1075", "1024");
}
}