use super::{approx_atan::approx_atan, Float256, FP492};
use crate::{
abs_bits, abs_bits_sticky,
consts::{FRAC_3_PI_2, FRAC_PI_2, FRAC_PI_4, PI},
f256, sign_bits_hi, BinEncAnySpecial, EXP_BIAS, HI_EXP_MASK,
HI_FRACTION_BITS, SIGNIFICAND_BITS, U256,
};
use core::cmp::{max, Ordering};
const LARGE_CUT_OFF: U256 = U256::new(
((EXP_BIAS + SIGNIFICAND_BITS) as u128) << HI_FRACTION_BITS,
0_u128,
);
const SMALL_CUT_OFF: f256 = f256 {
bits: U256::new(
0x3ff89250bfe1b082f4f9b8d4ce85ca7f,
0xcf68624f8a91d242f267bb52b5b4432a,
),
};
impl f256 {
#[must_use]
pub fn atan(&self) -> Self {
let abs_bits_self = abs_bits(self);
if (abs_bits_self.hi.0 | (abs_bits_self.lo.0 != 0) as u128)
> HI_EXP_MASK
{
return Self::NAN;
}
if abs_bits_self.hi >= LARGE_CUT_OFF.hi {
let mut res = FRAC_PI_2;
res.bits.hi.0 ^= sign_bits_hi(self);
return res;
}
if abs_bits_self <= SMALL_CUT_OFF.bits {
return *self;
}
match abs_bits_self.cmp(&Self::ONE.bits) {
Ordering::Less => Self::from(&approx_atan(&FP492::from(self))),
Ordering::Greater => {
let xr = Float256::from(self).recip();
let atan = [Float256::FRAC_PI_2, -Float256::FRAC_PI_2]
[self.sign() as usize]
- Float256::from(&approx_atan(&FP492::from(&xr)));
Self::from(&atan)
}
_ => {
[FRAC_PI_4, -FRAC_PI_4][self.sign() as usize]
}
}
}
#[must_use]
pub fn atan2(&self, other: &Self) -> Self {
let mut abs_bits_x = abs_bits(other);
let mut abs_bits_y = abs_bits(self);
let abs_bits_sticky_x = abs_bits_sticky(&abs_bits_x);
let abs_bits_sticky_y = abs_bits_sticky(&abs_bits_y);
if (abs_bits_sticky_x, abs_bits_sticky_y).any_special() {
if max(abs_bits_sticky_x, abs_bits_sticky_y) > HI_EXP_MASK {
return Self::NAN;
}
if abs_bits_sticky_x == 0_u128 {
return if abs_bits_sticky_y == 0 {
Self::ZERO
} else {
let mut res = FRAC_PI_2;
res.bits.hi.0 |= sign_bits_hi(self);
res
};
}
if abs_bits_sticky_y == 0_u128 {
return [Self::ZERO, PI][other.sign() as usize];
}
return match (self.sign(), other.sign()) {
(0, 0) => FRAC_PI_4,
(0, 1) => FRAC_3_PI_2,
(1, 0) => -FRAC_PI_4,
_ => FRAC_PI_4 - PI,
};
}
let sign_q = (self.sign() + other.sign()) % 2;
let mut atan = match abs_bits_y.cmp(&abs_bits_x) {
Ordering::Less => {
let mut q = Float256::from(self);
q /= &Float256::from(other);
Float256::from(&approx_atan(&FP492::from(&q)))
}
Ordering::Greater => {
let mut q = Float256::from(other);
q /= &Float256::from(self);
[Float256::FRAC_PI_2, -Float256::FRAC_PI_2][sign_q as usize]
- Float256::from(&approx_atan(&FP492::from(&q)))
}
_ => [Float256::FRAC_PI_2, -Float256::FRAC_PI_2][sign_q as usize],
};
match (self.sign(), other.sign()) {
(0, 1) => {
atan += &Float256::PI;
}
(1, 1) => {
atan -= &Float256::PI;
}
_ => {}
}
Self::from(&atan)
}
}
#[cfg(test)]
mod atan_tests {
use core::{ops::Neg, str::FromStr};
use super::*;
use crate::{
consts::{FRAC_1_PI, FRAC_PI_3, FRAC_PI_4, FRAC_PI_6},
EPSILON,
};
#[test]
fn calc_small_cutoff() {
let mut lf = f256::from(1e-36_f64);
let mut uf = f256::from(1e-35_f64);
assert_eq!(lf, lf.atan());
assert_ne!(uf, uf.atan());
let mut f = (lf + uf) / f256::TWO;
while lf < f && f < uf {
if f == f.atan() {
lf = f;
} else {
uf = f;
}
f = (lf + uf) / f256::TWO;
}
assert_eq!(f, f.atan());
assert_eq!(f, SMALL_CUT_OFF);
let g = f + f.ulp();
assert_ne!(g, g.atan());
}
#[test]
fn test_atan_inf() {
assert_eq!(f256::INFINITY.atan(), FRAC_PI_2);
}
#[test]
fn test_atan_large_cutoff() {
let f = f256 {
bits: LARGE_CUT_OFF,
};
assert_eq!(f.atan(), FRAC_PI_2);
}
#[test]
fn test_atan_zero() {
assert_eq!(f256::ZERO.atan(), f256::ZERO);
assert_eq!(f256::NEG_ZERO.atan(), f256::ZERO);
}
#[test]
fn test_atan_one() {
assert_eq!(f256::ONE.atan(), FRAC_PI_4);
assert_eq!(f256::NEG_ONE.atan(), -FRAC_PI_4);
}
#[test]
fn test_atan_sqrt_3() {
let t = f256::from(3);
let mut f = t.sqrt();
assert_eq!(f.atan(), FRAC_PI_3);
assert_eq!(f.neg().atan(), -FRAC_PI_3);
f /= t;
assert_eq!(f.atan(), FRAC_PI_6);
assert_eq!(f.neg().atan(), -FRAC_PI_6);
}
#[test]
fn test_atan_frac_1_pi() {
let f1 = FRAC_1_PI.atan();
let f2 = f256::ONE.atan2(&PI);
let d = f1 - f2;
assert!(d.abs() <= EPSILON);
}
#[test]
fn test_atan_frac_1_over_256() {
let x = f256::from(256).recip();
let ax = f256::from_str(
"3.90623013196697182762866531142438714035749011520285621521309514901134417e-3")
.unwrap();
let r = x.atan();
assert_eq!(ax, r);
}
#[test]
fn test_atan_frac_pi_2() {
let s = "1.00388482185388721414842394491713228829210446059487057472971282410801519";
let a = f256::from_str(s).unwrap();
let f1 = FRAC_PI_2.atan();
assert_eq!(f1, a, "{} != {}", f1, a);
let f2 = PI.atan2(&f256::TWO);
assert_eq!(f1, f2, "{} != {}", f1, f2);
}
#[test]
fn test_atan_frac_5_pi_4() {
let s = "1.32144796778372235539166569069508390109061014033053361477468861418765787";
let a = f256::from_str(s).unwrap();
let f = PI + FRAC_PI_4;
assert_eq!(f.atan(), a);
let f = f256::TEN * PI;
assert_eq!(f.atan2(&f256::from(8_f64)), a);
}
#[test]
fn test_atan_frac_51043_7() {
let s = "1.570659187521027203661619536335073835579283228441242208112611672132902725";
let a = f256::from_str(s).unwrap();
let n = f256::from(51043);
let d = f256::from(7);
let f = n / d;
assert_eq!(n.atan2(&d), a);
}
}