#![allow(clippy::indexing_slicing)]
#![allow(clippy::excessive_precision)]
#![allow(clippy::approx_constant)]
#![allow(clippy::eq_op)]
use crate::Real;
const ATANHI: [Real; 4] = [
4.63647609000806093515e-01,
7.85398163397448278999e-01,
9.82793723247329054082e-01,
1.57079632679489655800e+00,
];
const ATANLO: [Real; 4] = [
2.26987774529616870924e-17,
3.06161699786838301793e-17,
1.39033110312309984516e-17,
6.12323399573676603587e-17,
];
const AT: [Real; 11] = [
3.33333333333329318027e-01,
-1.99999999998764832476e-01,
1.42857142725034663711e-01,
-1.11111104054623557880e-01,
9.09088713343650656196e-02,
-7.69187620504482999495e-02,
6.66107313738753120669e-02,
-5.83357013379057348645e-02,
4.97687799461593236017e-02,
-3.65315727442169155270e-02,
1.62858201153657823623e-02,
];
pub const fn atan(x: Real) -> Real {
let mut x = x;
let mut ix = (Real::to_bits(x) >> 32) as u32;
let sign = ix >> 31;
ix &= 0x7fff_ffff;
if ix >= 0x4410_0000 {
if x.is_nan() {
return x;
}
let z = ATANHI[3] + Real::from_bits(0x0380_0000); return if sign != 0 { -z } else { z };
}
let id = if ix < 0x3fdc_0000 {
if ix < 0x3e40_0000 {
if ix < 0x0010_0000 { }
return x;
}
-1
} else {
x = x.abs();
if ix < 0x3ff30000 {
if ix < 0x3fe60000 {
x = (Real::from_bits(0x4000000000000000) * x - Real::from_bits(0x3FF0000000000000))
/ (Real::from_bits(0x4000000000000000) + x);
0
} else {
x = (x - Real::from_bits(0x3FF0000000000000))
/ (x + Real::from_bits(0x3FF0000000000000));
1
}
} else if ix < 0x40038000 {
x = (x - Real::from_bits(0x3ff8000000000000))
/ (Real::from_bits(0x3FF0000000000000) + Real::from_bits(0x3ff8000000000000) * x);
2
} else {
x = -Real::from_bits(0x3FF0000000000000) / x;
3
}
};
let z = x * x;
let w = z * z;
let s1 = z * (AT[0] + w * (AT[2] + w * (AT[4] + w * (AT[6] + w * (AT[8] + w * AT[10])))));
let s2 = w * (AT[1] + w * (AT[3] + w * (AT[5] + w * (AT[7] + w * AT[9]))));
if id < 0 {
return x - x * (s1 + s2);
}
let z = ATANHI[id as usize] - (x * (s1 + s2) - ATANLO[id as usize] - x);
if sign != 0 { -z } else { z }
}
#[cfg(all(test, feature = "std"))]
mod atan_tests {
use core::f64;
use super::atan;
#[test]
fn sanity_check() {
for (input, answer) in [
(3.0_f64.sqrt() / 3.0, f64::consts::FRAC_PI_6),
(1.0, f64::consts::FRAC_PI_4),
(3.0_f64.sqrt(), f64::consts::FRAC_PI_3),
(-3.0_f64.sqrt() / 3.0, -f64::consts::FRAC_PI_6),
(-1.0, -f64::consts::FRAC_PI_4),
(-3.0_f64.sqrt(), -f64::consts::FRAC_PI_3),
]
.iter()
{
assert!(
(atan(*input) - answer) / answer < 1e-5,
"\natan({:.4}/16) = {:.4}, actual: {}",
input * 16.0,
answer,
atan(*input)
);
}
}
#[test]
fn zero() {
assert_eq!(atan(0.0), 0.0);
}
#[test]
fn infinity() {
assert_eq!(atan(f64::INFINITY), f64::consts::FRAC_PI_2);
}
#[test]
fn minus_infinity() {
assert_eq!(atan(f64::NEG_INFINITY), -f64::consts::FRAC_PI_2);
}
#[test]
fn nan() {
assert!(atan(f64::NAN).is_nan());
}
}