1 | //! arctangent approximation for a single-precision float. |
2 | //! |
3 | //! Method described at: |
4 | //! <https://ieeexplore.ieee.org/document/6375931> |
5 | |
6 | use super::F32; |
7 | use core::f32::consts::FRAC_PI_2; |
8 | |
9 | impl F32 { |
10 | /// Approximates `atan(x)` approximation in radians with a maximum error of |
11 | /// `0.002`. |
12 | /// |
13 | /// Returns [`Self::NAN`] if the number is [`Self::NAN`]. |
14 | pub fn atan(self) -> Self { |
15 | FRAC_PI_2 * self.atan_norm() |
16 | } |
17 | |
18 | /// Approximates `atan(x)` normalized to the `[−1,1]` range with a maximum |
19 | /// error of `0.1620` degrees. |
20 | pub fn atan_norm(self) -> Self { |
21 | const SIGN_MASK: u32 = 0x8000_0000; |
22 | const B: f32 = 0.596_227; |
23 | |
24 | // Extract the sign bit |
25 | let ux_s = SIGN_MASK & self.to_bits(); |
26 | |
27 | // Calculate the arctangent in the first quadrant |
28 | let bx_a = (B * self).abs(); |
29 | let n = bx_a + self * self; |
30 | let atan_1q = n / (1.0 + bx_a + n); |
31 | |
32 | // Restore the sign bit and convert to float |
33 | Self::from_bits(ux_s | atan_1q.to_bits()) |
34 | } |
35 | } |
36 | |
37 | #[cfg (test)] |
38 | mod tests { |
39 | use super::F32; |
40 | use core::f32::consts; |
41 | |
42 | /// 0.1620 degrees in radians |
43 | const MAX_ERROR: f32 = 0.003; |
44 | |
45 | #[test ] |
46 | fn sanity_check() { |
47 | // Arctangent test vectors - `(input, output)` |
48 | let test_vectors: &[(f32, f32)] = &[ |
49 | (3.0_f32.sqrt() / 3.0, consts::FRAC_PI_6), |
50 | (1.0, consts::FRAC_PI_4), |
51 | (3.0_f32.sqrt(), consts::FRAC_PI_3), |
52 | (-(3.0_f32.sqrt()) / 3.0, -consts::FRAC_PI_6), |
53 | (-1.0, -consts::FRAC_PI_4), |
54 | (-(3.0_f32.sqrt()), -consts::FRAC_PI_3), |
55 | ]; |
56 | |
57 | for &(x, expected) in test_vectors { |
58 | let actual = F32(x).atan().0; |
59 | let delta = actual - expected; |
60 | |
61 | assert!( |
62 | delta <= MAX_ERROR, |
63 | "delta {} too large: {} vs {}" , |
64 | delta, |
65 | actual, |
66 | expected |
67 | ); |
68 | } |
69 | } |
70 | |
71 | #[test ] |
72 | fn zero() { |
73 | assert_eq!(F32::ZERO.atan(), F32::ZERO); |
74 | } |
75 | |
76 | #[test ] |
77 | fn nan() { |
78 | assert!(F32::NAN.atan().is_nan()); |
79 | } |
80 | } |
81 | |