1#![no_std]
5#![warn(missing_docs)]
6#![forbid(unsafe_code)]
7
8pub mod consts {
15 pub const PI: f32 = core::f32::consts::PI;
17 pub const TAU: f32 = core::f32::consts::TAU;
19 pub const FRAC_PI_2: f32 = core::f32::consts::FRAC_PI_2;
21}
22
23#[derive(Debug, Clone, Copy, PartialEq, Eq)]
25pub enum TrigError {
26 DomainError,
28 Undefined,
30 NonFiniteValue,
32}
33
34#[inline(always)]
39fn internal_sqrt(x: f32) -> f32 {
40 if x <= 0.0 { return 0.0; }
41 #[cfg(all(target_arch = "arm", target_feature = "vfp2"))]
42 { x.sqrt() }
43 #[cfg(not(all(target_arch = "arm", target_feature = "vfp2")))]
44 {
45 let mut r = f32::from_bits(((x.to_bits() >> 1) + 0x1FBB_4F2E) & 0x7FFF_FFFF);
46 r = 0.5 * (r + x / r);
47 r = 0.5 * (r + x / r);
48 r = 0.5 * (r + x / r);
49 r
50 }
51}
52
53#[inline]
54fn wrap_angle(x: f32) -> f32 {
55 let mut w = x % consts::TAU;
56 if w > consts::PI { w -= consts::TAU; }
57 if w <= -consts::PI { w += consts::TAU; }
58 w
59}
60
61fn base_sin(x: f32) -> f32 {
64 let x_deg = x * (180.0 / consts::PI);
65 let top = 4.0 * x_deg * (180.0 - x_deg);
66 let bottom = 40500.0 - x_deg * (180.0 - x_deg);
67 top / bottom
68}
69
70pub fn sin(x: f32) -> f32 {
76 if !x.is_finite() { return f32::NAN; }
77 let w = wrap_angle(x);
78 if w < 0.0 {
79 -base_sin(-w)
80 } else {
81 base_sin(w)
82 }
83}
84
85pub fn cos(x: f32) -> f32 {
87 sin(x + consts::FRAC_PI_2)
88}
89pub fn atan2(y: f32, x: f32) -> Result<f32, TrigError> {
91 if !y.is_finite() || !x.is_finite() { return Err(TrigError::NonFiniteValue); }
92 if x == 0.0 && y == 0.0 { return Err(TrigError::Undefined); }
93
94 let abs_y = y.abs();
95 let abs_x = x.abs();
96
97 let a = if abs_x > abs_y { abs_y / abs_x } else { abs_x / abs_y };
99 let s = a * a;
100 let mut r = ((-0.0464964749 * s + 0.15931422) * s - 0.327622764) * s * a + a;
101
102 if abs_y > abs_x { r = consts::FRAC_PI_2 - r; }
103 if x < 0.0 { r = consts::PI - r; }
104 if y < 0.0 { r = -r; }
105
106 Ok(r)
107}
108
109pub fn asin(x: f32) -> Result<f32, TrigError> {
111 if x < -1.0 || x > 1.0 { return Err(TrigError::DomainError); }
112 if x == 1.0 { return Ok(consts::FRAC_PI_2); }
113 if x == -1.0 { return Ok(-consts::FRAC_PI_2); }
114 atan2(x, internal_sqrt(1.0 - x * x))
115}
116
117pub fn acos(x: f32) -> Result<f32, TrigError> {
119 if x < -1.0 || x > 1.0 { return Err(TrigError::DomainError); }
120 Ok(consts::FRAC_PI_2 - asin(x)?)
121}
122
123#[cfg(test)]
124mod tests {
125 use super::*;
126 use core::f32::consts::PI;
127
128 const EPS: f32 = 2e-3; #[test]
131 fn test_sin_basic() {
132 assert!((sin(0.0)).abs() < EPS);
133 assert!((sin(PI / 2.0) - 1.0).abs() < EPS);
134 assert!((sin(PI)).abs() < EPS);
135 }
136
137 #[test]
138 fn test_cos_basic() {
139 assert!((cos(0.0) - 1.0).abs() < EPS);
140 assert!((cos(PI / 2.0)).abs() < EPS);
141 }
142
143 #[test]
144 fn test_reduction_angle() {
145 assert!((wrap_angle(3.0 * PI).abs() - PI).abs() < EPS);
146 }
147
148 #[test]
149 fn test_atan2() {
150 assert!((atan2(1.0, 1.0).unwrap() - PI / 4.0).abs() < EPS);
151 }
152
153 #[test]
154 fn test_asin_acos() {
155 assert!((asin(0.5).unwrap() - PI / 6.0).abs() < EPS);
156 assert!((acos(0.5).unwrap() - PI / 3.0).abs() < EPS);
157 }
158}