use super::exp::{exp_inner_common, exp_split};
use super::log::hi_lo_log_inner;
use super::{int_is_odd, is_int, is_odd_int, Exp, Log};
use crate::traits::Int as _;
pub(crate) fn pow<F: Log + Exp>(x: F, y: F) -> F {
let (nx, xedelta) = x.normalize_arg();
let (ny, _) = y.normalize_arg();
let xexp = nx.raw_exp();
let yexp = ny.raw_exp();
if yexp == F::RawExp::ZERO || nx == F::one() {
F::one()
} else if (yexp == F::MAX_RAW_EXP && ny.raw_mant() != F::Raw::ZERO)
|| (xexp == F::MAX_RAW_EXP && nx.raw_mant() != F::Raw::ZERO)
{
F::NAN
} else if xexp == F::RawExp::ZERO {
if is_odd_int(ny) {
if ny.sign() {
F::INFINITY.copysign(nx)
} else {
nx
}
} else {
if ny.sign() {
F::INFINITY
} else {
F::ZERO
}
}
} else if xexp == F::MAX_RAW_EXP {
if nx.sign() {
if is_odd_int(ny) {
if ny.sign() {
-F::ZERO
} else {
F::neg_infinity()
}
} else {
if ny.sign() {
F::ZERO
} else {
F::INFINITY
}
}
} else {
if ny.sign() {
F::ZERO
} else {
F::INFINITY
}
}
} else if yexp == F::MAX_RAW_EXP {
if nx == -F::one() {
F::one()
} else if ny.sign() {
if xexp < F::EXP_OFFSET {
F::INFINITY
} else {
F::ZERO
}
} else {
if xexp < F::EXP_OFFSET {
F::ZERO
} else {
F::INFINITY
}
}
} else if nx.sign() && !is_int(ny) {
F::NAN
} else {
let logx = hi_lo_log_inner(nx.abs(), xedelta).to_semi();
let ylx = (logx * y).to_norm();
let absz = if ylx.hi() >= F::exp_hi_th() {
F::INFINITY
} else if ylx.hi() <= F::exp_lo_th() {
F::ZERO
} else {
let (k, r_hi, r_lo) = exp_split(ylx.hi());
let r_lo = r_lo + ylx.lo();
exp_inner_common(k, r_hi, r_lo)
};
if nx.sign() && int_is_odd(ny) {
-absz
} else {
absz
}
}
}
#[cfg(test)]
mod tests {
use crate::traits::Float;
use crate::FloatMath;
fn test<F: Float + FloatMath>() {
use crate::pow;
let f = F::parse;
assert_is_nan!(pow(F::NAN, F::NAN));
assert_is_nan!(pow(F::ZERO, F::NAN));
assert_is_nan!(pow(-F::ZERO, F::NAN));
assert_is_nan!(pow(F::two(), F::NAN));
assert_is_nan!(pow(F::INFINITY, F::NAN));
assert_is_nan!(pow(F::neg_infinity(), F::NAN));
assert_is_nan!(pow(F::NAN, F::one()));
assert_is_nan!(pow(F::NAN, F::INFINITY));
assert_is_nan!(pow(F::NAN, F::neg_infinity()));
assert_is_nan!(pow(f("-3"), f("0.5")));
assert_total_eq!(pow(F::ZERO, f("-33")), F::INFINITY);
assert_total_eq!(pow(-F::ZERO, f("-33")), F::neg_infinity());
assert_total_eq!(pow(F::ZERO, f("-33.5")), F::INFINITY);
assert_total_eq!(pow(-F::ZERO, f("-33.5")), F::INFINITY);
assert_total_eq!(pow(F::ZERO, f("-34")), F::INFINITY);
assert_total_eq!(pow(-F::ZERO, f("-34")), F::INFINITY);
assert_total_eq!(pow(F::ZERO, f("33")), F::ZERO);
assert_total_eq!(pow(-F::ZERO, f("33")), -F::ZERO);
assert_total_eq!(pow(F::ZERO, f("33.5")), F::ZERO);
assert_total_eq!(pow(-F::ZERO, f("33.5")), F::ZERO);
assert_total_eq!(pow(F::ZERO, f("34.0")), F::ZERO);
assert_total_eq!(pow(-F::ZERO, f("34.0")), F::ZERO);
assert_total_eq!(pow(F::ZERO, F::INFINITY), F::ZERO);
assert_total_eq!(pow(-F::ZERO, F::INFINITY), F::ZERO);
assert_total_eq!(pow(F::ZERO, F::neg_infinity()), F::INFINITY);
assert_total_eq!(pow(-F::ZERO, F::neg_infinity()), F::INFINITY);
assert_total_eq!(pow(F::one(), F::ZERO), F::one());
assert_total_eq!(pow(F::one(), -F::ZERO), F::one());
assert_total_eq!(pow(F::one(), f("33")), F::one());
assert_total_eq!(pow(F::one(), f("-33")), F::one());
assert_total_eq!(pow(F::one(), f("33.5")), F::one());
assert_total_eq!(pow(F::one(), f("-33.5")), F::one());
assert_total_eq!(pow(F::one(), f("34")), F::one());
assert_total_eq!(pow(F::one(), f("-34.0")), F::one());
assert_total_eq!(pow(F::one(), F::INFINITY), F::one());
assert_total_eq!(pow(F::one(), F::neg_infinity()), F::one());
assert_total_eq!(pow(F::one(), F::NAN), F::one());
assert_total_eq!(pow(-F::one(), F::INFINITY), F::one());
assert_total_eq!(pow(-F::one(), F::neg_infinity()), F::one());
assert_total_eq!(pow(f("0.5"), F::INFINITY), F::ZERO);
assert_total_eq!(pow(f("0.5"), F::neg_infinity()), F::INFINITY);
assert_total_eq!(pow(f("-0.5"), F::INFINITY), F::ZERO);
assert_total_eq!(pow(f("-0.5"), F::neg_infinity()), F::INFINITY);
assert_total_eq!(pow(f("1.5"), F::INFINITY), F::INFINITY);
assert_total_eq!(pow(f("1.5"), F::neg_infinity()), F::ZERO);
assert_total_eq!(pow(f("-1.5"), F::INFINITY), F::INFINITY);
assert_total_eq!(pow(f("-1.5"), F::neg_infinity()), F::ZERO);
assert_total_eq!(pow(F::INFINITY, F::ZERO), F::one());
assert_total_eq!(pow(F::INFINITY, -F::ZERO), F::one());
assert_total_eq!(pow(F::INFINITY, f("33")), F::INFINITY);
assert_total_eq!(pow(F::INFINITY, f("-33")), F::ZERO);
assert_total_eq!(pow(F::INFINITY, f("33.5")), F::INFINITY);
assert_total_eq!(pow(F::INFINITY, f("-33.5")), F::ZERO);
assert_total_eq!(pow(F::INFINITY, f("34.0")), F::INFINITY);
assert_total_eq!(pow(F::INFINITY, f("-34.0")), F::ZERO);
assert_total_eq!(pow(F::neg_infinity(), F::ZERO), F::one());
assert_total_eq!(pow(F::neg_infinity(), -F::ZERO), F::one());
assert_total_eq!(pow(F::neg_infinity(), f("33")), F::neg_infinity());
assert_total_eq!(pow(F::neg_infinity(), f("-33")), -F::ZERO);
assert_total_eq!(pow(F::neg_infinity(), f("33.5")), F::INFINITY);
assert_total_eq!(pow(F::neg_infinity(), f("-33.5")), F::ZERO);
assert_total_eq!(pow(F::neg_infinity(), f("34.0")), F::INFINITY);
assert_total_eq!(pow(F::neg_infinity(), f("-34.0")), F::ZERO);
assert_total_eq!(pow(F::two(), F::two()), f("4"));
assert_total_eq!(pow(F::two(), -F::two()), f("0.25"));
assert_total_eq!(pow(-F::two(), f("3")), f("-8"));
assert_total_eq!(pow(-F::two(), f("-3")), f("-0.125"));
assert_total_eq!(pow(f("3.5"), f("3")), f("42.875"));
assert_total_eq!(pow(f("10"), f("4")), f("10000"));
}
#[test]
fn test_f32() {
test::<f32>();
}
#[cfg(feature = "soft-float")]
#[test]
fn test_soft_f32() {
test::<crate::SoftF32>();
}
#[test]
fn test_f64() {
test::<f64>();
}
#[cfg(feature = "soft-float")]
#[test]
fn test_soft_f64() {
test::<crate::SoftF64>();
}
}