use crate::double::{DenormDouble, SemiDouble};
use crate::traits::{CastInto as _, FloatConsts, Int as _, Like};
pub(crate) trait Atan<L = Like<Self>>: FloatConsts {
fn frac_pi_2_hi() -> Self;
fn frac_pi_2_lo() -> Self;
fn frac_3pi_4() -> Self;
fn atan_poly(x2: Self) -> (Self, Self);
}
pub(crate) fn atan<F: Atan>(x: F) -> F {
let e = x.raw_exp();
if e == F::MAX_RAW_EXP {
if x.raw_mant() == F::Raw::ZERO {
F::frac_pi_2().copysign(x)
} else {
x
}
} else if e <= F::MANT_BITS.into() {
x
} else {
atan_inner(x).to_single()
}
}
pub(crate) fn atan2<F: Atan>(y: F, x: F) -> F {
let (ny, nx) = if y.raw_exp() <= F::MANT_BITS.into() || x.raw_exp() <= F::MANT_BITS.into() {
let scale = F::exp2i_fast((F::MANT_BITS * 2 + 1).cast_into());
(y * scale, x * scale)
} else {
(y, x)
};
let nxexp = nx.raw_exp();
let nyexp = ny.raw_exp();
if (nxexp == F::MAX_RAW_EXP && nx.raw_mant() != F::Raw::ZERO)
|| (nyexp == F::MAX_RAW_EXP && ny.raw_mant() != F::Raw::ZERO)
{
F::NAN
} else if nxexp == F::MAX_RAW_EXP && nyexp == F::MAX_RAW_EXP {
match (nx.sign(), ny.sign()) {
(false, false) => F::frac_pi_4(),
(false, true) => -F::frac_pi_4(),
(true, false) => F::frac_3pi_4(),
(true, true) => -F::frac_3pi_4(),
}
} else if nxexp == F::MAX_RAW_EXP {
if nx.sign() {
F::pi().copysign(ny)
} else {
F::ZERO.copysign(ny)
}
} else if nyexp == F::MAX_RAW_EXP {
F::frac_pi_2().copysign(ny)
} else if nyexp == F::RawExp::ZERO {
if nx.sign() {
F::pi().copysign(ny)
} else {
ny
}
} else if nxexp == F::RawExp::ZERO {
F::frac_pi_2().copysign(ny)
} else if !nx.sign()
&& nxexp > nyexp
&& (nxexp - nyexp) >= ((F::MAX_RAW_EXP >> 1) - F::MANT_BITS.into())
{
ny / nx
} else {
atan2_inner(ny, nx).to_single()
}
}
pub(super) fn atan_inner<F: Atan>(x: F) -> DenormDouble<F> {
if x.abs() <= F::one() {
atan_inner_common(SemiDouble::new(x))
} else {
let y = DenormDouble::new_recip(x);
let t1 = atan_inner_common(y.to_semi());
let off = DenormDouble::new(F::frac_pi_2_hi().copysign(x), F::frac_pi_2_lo().copysign(x));
off.qsub2(t1)
}
}
pub(super) fn atan2_inner<F: Atan>(mut n: F, mut d: F) -> DenormDouble<F> {
let ysgn = n.sign();
let xsgn = d.sign();
let mut off = F::ZERO;
if xsgn {
off = F::two().set_sign(ysgn);
}
if n.abs() > d.abs() {
core::mem::swap(&mut n, &mut d);
n = -n;
off = off + F::one().set_sign(ysgn ^ xsgn);
}
let z = DenormDouble::new_div11(n, d);
let t1 = atan_inner_common(z.to_semi());
let t2 = DenormDouble::new(F::frac_pi_2_hi() * off, F::frac_pi_2_lo() * off);
t2.qadd2(t1)
}
pub(super) fn atan_inner_common<F: Atan>(x: SemiDouble<F>) -> DenormDouble<F> {
let x2 = x.square();
let (k3, t1) = F::atan_poly(x2.to_single());
let t2 = SemiDouble::new_qadd11(k3, t1);
let x3 = x * x2.to_semi();
let t3 = x3.to_semi() * t2;
x.to_denorm().qadd2(t3)
}
#[cfg(test)]
mod tests {
use super::Atan;
use crate::FloatMath;
fn test_atan<F: Atan + FloatMath>() {
use crate::atan;
assert_is_nan!(atan(F::NAN));
assert_total_eq!(atan(F::INFINITY), F::frac_pi_2());
assert_total_eq!(atan(F::neg_infinity()), -F::frac_pi_2());
assert_total_eq!(atan(F::ZERO), F::ZERO);
assert_total_eq!(atan(-F::ZERO), -F::ZERO);
}
fn test_atan2<F: Atan + FloatMath>() {
use crate::atan2;
assert_is_nan!(atan2(F::NAN, F::one()));
assert_is_nan!(atan2(F::NAN, F::ZERO));
assert_is_nan!(atan2(F::NAN, F::INFINITY));
assert_is_nan!(atan2(F::NAN, F::NAN));
assert_is_nan!(atan2(F::INFINITY, F::NAN));
assert_is_nan!(atan2(F::ZERO, F::NAN));
assert_is_nan!(atan2(F::one(), F::NAN));
assert_total_eq!(atan2(F::ZERO, F::ZERO), F::ZERO);
assert_total_eq!(atan2(-F::ZERO, F::ZERO), -F::ZERO);
assert_total_eq!(atan2(F::ZERO, F::one()), F::ZERO);
assert_total_eq!(atan2(-F::ZERO, F::one()), -F::ZERO);
assert_total_eq!(atan2(F::ZERO, F::INFINITY), F::ZERO);
assert_total_eq!(atan2(-F::ZERO, F::INFINITY), -F::ZERO);
assert_total_eq!(atan2(F::ZERO, -F::ZERO), F::pi());
assert_total_eq!(atan2(-F::ZERO, -F::ZERO), -F::pi());
assert_total_eq!(atan2(F::ZERO, -F::one()), F::pi());
assert_total_eq!(atan2(-F::ZERO, -F::one()), -F::pi());
assert_total_eq!(atan2(F::INFINITY, F::ZERO), F::frac_pi_2());
assert_total_eq!(atan2(F::INFINITY, -F::ZERO), F::frac_pi_2());
assert_total_eq!(atan2(F::INFINITY, F::one()), F::frac_pi_2());
assert_total_eq!(atan2(F::INFINITY, -F::one()), F::frac_pi_2());
assert_total_eq!(atan2(F::neg_infinity(), F::ZERO), -F::frac_pi_2());
assert_total_eq!(atan2(F::neg_infinity(), -F::ZERO), -F::frac_pi_2());
assert_total_eq!(atan2(F::neg_infinity(), F::one()), -F::frac_pi_2());
assert_total_eq!(atan2(F::neg_infinity(), -F::one()), -F::frac_pi_2());
assert_total_eq!(atan2(F::ZERO, F::INFINITY), F::ZERO);
assert_total_eq!(atan2(-F::ZERO, F::INFINITY), -F::ZERO);
assert_total_eq!(atan2(F::one(), F::INFINITY), F::ZERO);
assert_total_eq!(atan2(-F::one(), F::INFINITY), -F::ZERO);
assert_total_eq!(atan2(F::ZERO, F::neg_infinity()), F::pi());
assert_total_eq!(atan2(-F::ZERO, F::neg_infinity()), -F::pi());
assert_total_eq!(atan2(F::one(), F::neg_infinity()), F::pi());
assert_total_eq!(atan2(-F::one(), F::neg_infinity()), -F::pi());
assert_total_eq!(atan2(F::INFINITY, F::INFINITY), F::frac_pi_4());
assert_total_eq!(atan2(F::neg_infinity(), F::INFINITY), -F::frac_pi_4());
assert_total_eq!(atan2(F::INFINITY, F::neg_infinity()), F::frac_3pi_4());
assert_total_eq!(
atan2(F::neg_infinity(), F::neg_infinity()),
-F::frac_3pi_4()
);
}
#[test]
fn test_f32() {
test_atan::<f32>();
test_atan2::<f32>();
}
#[cfg(feature = "soft-float")]
#[test]
fn test_soft_f32() {
test_atan::<crate::SoftF32>();
test_atan2::<crate::SoftF32>();
}
#[test]
fn test_f64() {
test_atan::<f64>();
test_atan2::<f64>();
}
#[cfg(feature = "soft-float")]
#[test]
fn test_soft_f64() {
test_atan::<crate::SoftF64>();
test_atan2::<crate::SoftF64>();
}
}