use crate::double::{
acos64, asin64, atan2_64, atan64, exp64, k_cos64, k_sin64, ln64, reduce_pio2_64,
};
use crate::ops::{round, sqrt};
pub(crate) const PIO2_1: f32 = 1.570_312_5;
pub(crate) const PIO2_2: f32 = 4.837_512_969_970_703_125e-4;
pub(crate) const PIO2_3: f32 = 7.549_789_948_768_648e-8;
pub(crate) const FRAC_2_PI: f32 = core::f32::consts::FRAC_2_PI;
pub(crate) const SIN_P: [f32; 3] = [-1.951_529_589_1e-4, 8.332_160_873_6e-3, -1.666_665_461_1e-1];
pub(crate) const COS_P: [f32; 3] = [
2.443_315_711_809_948e-5,
-1.388_731_625_493_765e-3,
4.166_664_568_298_827e-2,
];
#[inline(always)]
fn sin_poly(r: f32) -> f32 {
let z = r * r;
let p = ((SIN_P[0] * z + SIN_P[1]) * z + SIN_P[2]) * z * r;
r + p
}
#[inline(always)]
fn cos_poly(r: f32) -> f32 {
let z = r * r;
let p = ((COS_P[0] * z + COS_P[1]) * z + COS_P[2]) * z * z;
1.0 - 0.5 * z + p
}
#[inline(always)]
fn reduce_pio2(x: f32) -> (i32, f32) {
let kf = round(x * FRAC_2_PI);
let r = ((x - kf * PIO2_1) - kf * PIO2_2) - kf * PIO2_3;
let k = (kf as i32) & 3;
(k, r)
}
#[inline(always)]
pub(crate) fn canon(v: f32) -> f32 {
if v.is_nan() {
f32::NAN
} else {
v
}
}
#[must_use]
pub fn sin(x: f32) -> f32 {
if !x.is_finite() {
return f32::NAN;
}
let (k, r) = reduce_pio2(x);
canon(match k {
0 => sin_poly(r),
1 => cos_poly(r),
2 => -sin_poly(r),
_ => -cos_poly(r),
})
}
#[must_use]
pub fn cos(x: f32) -> f32 {
if !x.is_finite() {
return f32::NAN;
}
let (k, r) = reduce_pio2(x);
canon(match k {
0 => cos_poly(r),
1 => -sin_poly(r),
2 => -cos_poly(r),
_ => sin_poly(r),
})
}
#[must_use]
pub fn sin_cos(x: f32) -> (f32, f32) {
if !x.is_finite() {
return (f32::NAN, f32::NAN);
}
let (k, r) = reduce_pio2(x);
let s = sin_poly(r);
let c = cos_poly(r);
let (s, c) = match k {
0 => (s, c),
1 => (c, -s),
2 => (-s, -c),
_ => (-c, s),
};
(canon(s), canon(c))
}
pub(crate) const LOG2E: f32 = core::f32::consts::LOG2_E;
pub(crate) const LN2_HI: f32 = 0.693_145_751_953_125;
pub(crate) const LN2_LO: f32 = 1.428_606_765_330_187e-6;
pub(crate) const EXP_P: [f32; 5] = [
1.987_569_150_0e-4,
1.398_199_950_7e-3,
8.333_451_907_3e-3,
4.166_579_589_4e-2,
1.666_666_545_9e-1,
];
pub(crate) const EXP_HI: f32 = 88.722_84;
pub(crate) const EXP_LO: f32 = -103.972_08;
#[inline(always)]
fn pow2i(k: i32) -> f32 {
debug_assert!((-126..=127).contains(&k));
f32::from_bits(((k + 127) as u32) << 23)
}
#[must_use]
pub fn exp(x: f32) -> f32 {
if x.is_nan() {
return f32::NAN;
}
if x > EXP_HI {
return f32::INFINITY;
}
if x < EXP_LO {
return 0.0;
}
let kf = round(x * LOG2E);
let k = kf as i32;
let r = (x - kf * LN2_HI) - kf * LN2_LO;
let p = (((((EXP_P[0] * r + EXP_P[1]) * r + EXP_P[2]) * r + EXP_P[3]) * r + EXP_P[4]) * r
+ 0.5)
* r
* r
+ r
+ 1.0;
if k > 127 {
p * pow2i(127) * pow2i(k - 127)
} else if k < -126 {
p * pow2i(-126) * pow2i(k + 126)
} else {
p * pow2i(k)
}
}
pub(crate) const LG1: f32 = 0.666_666_626_93;
pub(crate) const LG2: f32 = 0.400_009_721_52;
pub(crate) const LG3: f32 = 0.284_987_866_88;
pub(crate) const LG4: f32 = 0.242_790_788_41;
pub(crate) const SQRT2_BITS: u32 = 0x3fb5_04f3;
#[must_use]
pub fn ln(x: f32) -> f32 {
if x.is_nan() || x < 0.0 {
return f32::NAN;
}
if x == 0.0 {
return f32::NEG_INFINITY;
}
if x.is_infinite() {
return f32::INFINITY;
}
let mut bits = x.to_bits();
let mut k: i32 = 0;
if bits < 0x0080_0000 {
let scaled = x * 33_554_432.0; bits = scaled.to_bits();
k -= 25;
}
k += ((bits >> 23) as i32) - 127;
bits = (bits & 0x007f_ffff) | 0x3f80_0000; if bits >= SQRT2_BITS {
bits -= 0x0080_0000;
k += 1;
}
let m = f32::from_bits(bits);
let f = m - 1.0;
let s = f / (2.0 + f);
let z = s * s;
let w = z * z;
let t1 = w * (LG2 + w * LG4);
let t2 = z * (LG1 + w * LG3);
let r = t2 + t1;
let hfsq = 0.5 * f * f;
let kf = k as f32;
kf * LN2_HI - ((hfsq - (s * (hfsq + r) + kf * LN2_LO)) - f)
}
#[must_use]
pub fn powf(x: f32, y: f32) -> f32 {
if y == 0.0 {
return 1.0;
}
if x.is_nan() || y.is_nan() || x < 0.0 {
return f32::NAN;
}
if x == 0.0 {
return if y > 0.0 { 0.0 } else { f32::INFINITY };
}
exp64(f64::from(y) * ln64(f64::from(x))) as f32
}
#[must_use]
pub fn powi(x: f32, n: i32) -> f32 {
if n == 0 {
return 1.0;
}
if x.is_nan() {
return f32::NAN;
}
let mut base = if n < 0 { 1.0 / x } else { x };
let mut e = n.unsigned_abs();
let mut acc = 1.0f32;
while e > 0 {
if e & 1 == 1 {
acc *= base;
}
e >>= 1;
if e > 0 {
base *= base;
}
}
acc
}
#[must_use]
pub fn cbrt(x: f32) -> f32 {
if x == 0.0 || !x.is_finite() {
return x;
}
let ax = x.abs();
let (ax, post) = if ax.to_bits() < 0x0080_0000 {
(ax * 16_777_216.0, 1.0 / 256.0)
} else if ax.to_bits() >= 0x6f80_0000 {
(ax * (1.0 / 16_777_216.0), 256.0)
} else {
(ax, 1.0)
};
let mut y = f32::from_bits(ax.to_bits() / 3 + 0x2a51_37a0);
for _ in 0..3 {
let y2 = y * y;
y -= (y2 * y - ax) / (3.0 * y2);
}
let y = y * post;
if x < 0.0 {
-y
} else {
y
}
}
#[must_use]
pub fn hypot(x: f32, y: f32) -> f32 {
let ax = x.abs();
let ay = y.abs();
if ax.is_infinite() || ay.is_infinite() {
return f32::INFINITY;
}
if ax.is_nan() || ay.is_nan() {
return f32::NAN;
}
let m = if ax > ay { ax } else { ay };
if m == 0.0 {
return 0.0;
}
let (scale, unscale) = if m > 1.0e18 {
(
1.0 / 18_446_744_073_709_551_616.0,
18_446_744_073_709_551_616.0,
) } else if m < 1.0e-18 {
(
18_446_744_073_709_551_616.0,
1.0 / 18_446_744_073_709_551_616.0,
)
} else {
(1.0, 1.0)
};
let sx = ax * scale;
let sy = ay * scale;
sqrt(sx * sx + sy * sy) * unscale
}
#[must_use]
pub fn atan(x: f32) -> f32 {
atan64(f64::from(x)) as f32
}
#[must_use]
pub fn atan2(y: f32, x: f32) -> f32 {
atan2_64(f64::from(y), f64::from(x)) as f32
}
#[must_use]
pub fn asin(x: f32) -> f32 {
asin64(f64::from(x)) as f32
}
#[must_use]
pub fn acos(x: f32) -> f32 {
acos64(f64::from(x)) as f32
}
#[must_use]
pub fn tan(x: f32) -> f32 {
if !x.is_finite() {
return f32::NAN;
}
let (k, r) = reduce_pio2_64(f64::from(x));
let (s, c) = (k_sin64(r), k_cos64(r));
let t = if k & 1 == 0 { s / c } else { -c / s };
canon(t as f32)
}
#[must_use]
pub fn tanh(x: f32) -> f32 {
if x.is_nan() {
return f32::NAN;
}
let ax = x.abs();
if ax < 6.103_515_625e-5 {
return x;
}
if ax > 10.0 {
return if x < 0.0 { -1.0 } else { 1.0 };
}
let e = exp64(2.0 * f64::from(ax));
let t = ((e - 1.0) / (e + 1.0)) as f32;
if x < 0.0 {
-t
} else {
t
}
}