| 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 | |