use crate::common::*;
use crate::compound::compound_m1f::compoundf_expf_poly;
use crate::compound::compoundf::{
COMPOUNDF_EXP2_T, COMPOUNDF_EXP2_U, LOG2P1_COMPOUNDF_INV, LOG2P1_COMPOUNDF_LOG2_INV,
};
use crate::rounding::CpuRoundTiesEven;
use std::hint::black_box;
#[inline]
fn powm1f_log2_fast(x: f64) -> f64 {
let mut v = x.to_bits();
let m: u64 = v & 0xfffffffffffffu64;
let e: i64 = (v >> 52) as i64 - 0x3ff + (m >= 0x6a09e667f3bcdu64) as i64;
v = v.wrapping_sub((e << 52) as u64);
let t = f64::from_bits(v);
v = (f64::from_bits(v) + 2.0).to_bits(); let i = (v >> 45) as i32 - 0x2002d; let r = f64::from_bits(LOG2P1_COMPOUNDF_INV[i as usize]);
let z = dd_fmla(r, t, -1.0); let p = crate::compound::compoundf::log2p1_polyeval_1(z);
e as f64 + (f64::from_bits(LOG2P1_COMPOUNDF_LOG2_INV[i as usize].1) + p)
}
pub fn f_powm1f(x: f32, y: f32) -> f32 {
let ax: u32 = x.to_bits().wrapping_shl(1);
let ay: u32 = y.to_bits().wrapping_shl(1);
if ax == 0 || ax >= 0xffu32 << 24 || ay == 0 || ay >= 0xffu32 << 24 {
if x.is_nan() || y.is_nan() {
return f32::NAN;
}
if x.is_infinite() {
return if x.is_sign_positive() {
if y.is_infinite() {
return f32::INFINITY;
} else if y > 0.0 {
f32::INFINITY } else if y < 0.0 {
-1.0 } else {
f32::NAN }
} else {
if y.is_infinite() {
return -1.0;
}
if is_integerf(y) {
let pow = if y as i32 % 2 == 0 {
f32::INFINITY
} else {
f32::NEG_INFINITY
};
pow - 1.0
} else {
f32::NAN }
};
}
if y.is_infinite() {
return if x.abs() > 1.0 {
if y.is_sign_positive() {
f32::INFINITY
} else {
-1.0
}
} else if x.abs() < 1.0 {
if y.is_sign_positive() {
-1.0
} else {
f32::INFINITY
}
} else {
f32::NAN };
}
if x == 0.0 {
return if y > 0.0 {
-1.0 } else if y < 0.0 {
f32::INFINITY } else {
0.0 };
}
}
let y_integer = is_integerf(y);
let mut negative_parity: bool = false;
let mut x = x;
if x < 0.0 {
if !y_integer {
return f32::NAN; }
x = x.abs();
if is_odd_integerf(y) {
negative_parity = true;
}
}
let xd = x as f64;
let yd = y as f64;
let tx = xd.to_bits();
let ty = yd.to_bits();
let l: f64 = powm1f_log2_fast(f64::from_bits(tx));
let dt = l * f64::from_bits(ty);
let t: u64 = dt.to_bits();
if (t.wrapping_shl(1)) >= (0x406u64 << 53) {
if t >= 0x3018bu64 << 46 {
return -1.;
} else if (t >> 63) == 0 {
return black_box(f32::from_bits(0x7e800000)) * black_box(f32::from_bits(0x7e800000));
}
}
let res = powm1_exp2m1_fast(f64::from_bits(t));
if negative_parity {
(-res - 2.) as f32
} else {
res as f32
}
}
#[inline]
pub(crate) fn powm1_exp2m1_fast(t: f64) -> f64 {
let k = t.cpu_round_ties_even(); let mut r = t - k; let mut v: f64 = 3.015625 + r; let i: i32 = (v.to_bits() >> 46) as i32 - 0x10010; r -= f64::from_bits(COMPOUNDF_EXP2_T[i as usize]); let mut s = f64::from_bits(COMPOUNDF_EXP2_U[i as usize].1);
let su = (k as i64).wrapping_shl(52);
s = f64::from_bits(s.to_bits().wrapping_add(su as u64));
let q_poly = compoundf_expf_poly(r);
v = q_poly;
#[cfg(any(
all(
any(target_arch = "x86", target_arch = "x86_64"),
target_feature = "fma"
),
target_arch = "aarch64"
))]
{
v = f_fmla(v, s, s - 1f64);
}
#[cfg(not(any(
all(
any(target_arch = "x86", target_arch = "x86_64"),
target_feature = "fma"
),
target_arch = "aarch64"
)))]
{
use crate::double_double::DoubleDouble;
let p0 = DoubleDouble::from_full_exact_add(s, -1.);
let z = DoubleDouble::from_exact_mult(v, s);
v = DoubleDouble::add(z, p0).to_f64();
}
v
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_powm1f() {
assert_eq!(f_powm1f(1.83329e-40, 2.4645883e-32), -2.2550315e-30);
assert_eq!(f_powm1f(f32::INFINITY, f32::INFINITY), f32::INFINITY);
assert_eq!(f_powm1f(-3.375, -9671689000000000000000000.), -1.);
assert_eq!(f_powm1f(3., 2.), 8.);
assert_eq!(f_powm1f(3., 3.), 26.);
assert_eq!(f_powm1f(5., 2.), 24.);
assert_eq!(f_powm1f(5., -2.), 1. / 25. - 1.);
assert_eq!(f_powm1f(-5., 2.), 24.);
assert_eq!(f_powm1f(-5., 3.), -126.);
assert_eq!(
f_powm1f(196560., 0.000000000000000000000000000000000000001193773),
1.455057e-38
);
assert!(f_powm1f(f32::NAN, f32::INFINITY).is_nan());
assert!(f_powm1f(f32::INFINITY, f32::NAN).is_nan());
}
}