1//! arccos approximation for a single-precision float.
2//!
3//! Method described at:
4//! <https://math.stackexchange.com/questions/2908908/express-arccos-in-terms-of-arctan>
5
6use super::F32;
7use core::f32::consts::PI;
8
9impl F32 {
10 /// Computes `acos(x)` approximation in radians in the range `[0, pi]`.
11 pub(crate) fn acos(self) -> Self {
12 if self > 0.0 {
13 ((Self::ONE - self * self).sqrt() / self).atan()
14 } else if self == 0.0 {
15 Self(PI / 2.0)
16 } else {
17 ((Self::ONE - self * self).sqrt() / self).atan() + PI
18 }
19 }
20}
21
22#[cfg(test)]
23mod tests {
24 use super::F32;
25 use core::f32::consts;
26
27 const MAX_ERROR: f32 = 0.03;
28
29 #[test]
30 fn sanity_check() {
31 // Arccosine test vectors - `(input, output)`
32 let test_vectors: &[(f32, f32)] = &[
33 (2.000, f32::NAN),
34 (1.000, 0.0),
35 (0.866, consts::FRAC_PI_6),
36 (0.707, consts::FRAC_PI_4),
37 (0.500, consts::FRAC_PI_3),
38 (f32::EPSILON, consts::FRAC_PI_2),
39 (0.000, consts::FRAC_PI_2),
40 (-f32::EPSILON, consts::FRAC_PI_2),
41 (-0.500, 2.0 * consts::FRAC_PI_3),
42 (-0.707, 3.0 * consts::FRAC_PI_4),
43 (-0.866, 5.0 * consts::FRAC_PI_6),
44 (-1.000, consts::PI),
45 (-2.000, f32::NAN),
46 ];
47
48 for &(x, expected) in test_vectors {
49 let actual = F32(x).acos();
50 if expected.is_nan() {
51 assert!(
52 actual.is_nan(),
53 "acos({}) returned {}, should be NAN",
54 x,
55 actual
56 );
57 } else {
58 let delta = (actual - expected).abs();
59
60 assert!(
61 delta <= MAX_ERROR,
62 "delta {} too large: {} vs {}",
63 delta,
64 actual,
65 expected
66 );
67 }
68 }
69 }
70}
71