1pub use ape_table_trig_macros::*;
25
26pub use core::f32::consts::PI as PI_F32;
27pub use core::f64::consts::PI as PI_F64;
28
29pub const QUART_CIRC_F32: f32 = 0.5 * PI_F32;
31pub const QUART_CIRC_F64: f64 = 0.5 * PI_F64;
33
34pub const HALF_CIRC_F32: f32 = PI_F32;
36pub const HALF_CIRC_F64: f64 = PI_F64;
38
39pub const FULL_CIRC_F32: f32 = 2.0 * PI_F32;
41pub const FULL_CIRC_F64: f64 = 2.0 * PI_F64;
43
44pub const GEN_LIMIT_F32: f32 = QUART_CIRC_F32;
48pub const GEN_LIMIT_F64: f64 = QUART_CIRC_F64;
50
51#[inline]
52pub fn abs_f32(float: f32) -> f32 {
54 f32::from_ne_bytes(
56 (u32::from_ne_bytes(
57 float.to_ne_bytes()
58 ) & (u32::MAX >> 1)).to_ne_bytes()
59 )
60}
61
62#[inline]
63pub fn rem_32(a: f32, b: f32) -> f32 {
65 let a_over_b = ((a / b) as u32) as f32;
66
67 a - (a_over_b * b)
68}
69
70#[inline]
71pub fn rem_64(a: f64, b: f64) -> f64 {
73 let a_over_b = ((a / b) as u64) as f64;
74
75 a - (a_over_b * b)
76}
77
78#[inline]
79pub fn abs_f64(float: f64) -> f64 {
81 f64::from_ne_bytes(
83 (u64::from_ne_bytes(
84 float.to_ne_bytes()
85 ) & (u64::MAX >> 1)).to_ne_bytes()
86 )
87}
88
89pub struct TrigTableF32 {
91 table: &'static [f32],
92}
93
94impl TrigTableF32 {
95 pub fn new(table: &'static [f32]) -> Self {
97 Self {
98 table
99 }
100 }
101
102 pub fn sin(&self, radians: f32) -> f32 {
104 let is_negative = radians < 0.0;
105 let radians = abs_f32(radians);
106
107 let rad_mod = rem_32(radians, GEN_LIMIT_F32);
108
109 let is_reverse = ((radians / GEN_LIMIT_F32) as u32) % 2 == 1;
111
112 let table_len = self.table.len();
113
114 let index = (((rad_mod / GEN_LIMIT_F32) * (table_len as f32)) + 0.5) as usize;
116
117 let index = match is_reverse {
119 false => index,
120 true => table_len - index,
121 };
122
123 let sin = match index >= table_len {
124 false => self.table[index],
125 true => 1.0,
126 };
127
128 let is_negative = is_negative ^ (((radians / (2.0 * GEN_LIMIT_F32)) as u32) % 2 == 1);
130
131 match is_negative {
132 true => -sin,
133 false => sin,
134 }
135 }
136
137 #[inline]
138 pub fn cos(&self, radians: f32) -> f32 {
140 self.sin(radians + QUART_CIRC_F32)
141 }
142
143 #[inline]
144 pub fn tan(&self, radians: f32) -> f32 {
146 self.sin(radians) / self.cos(radians)
147 }
148}
149
150pub struct TrigTableF64 {
152 table: &'static [f64],
153}
154
155impl TrigTableF64 {
156 pub fn new(table: &'static [f64]) -> Self {
158 Self {
159 table
160 }
161 }
162
163 pub fn sin(&self, radians: f64) -> f64 {
165 let is_negative = radians < 0.0;
166 let radians = abs_f64(radians);
167
168 let rad_mod = rem_64(radians, GEN_LIMIT_F64);
169
170 let is_reverse = ((radians / GEN_LIMIT_F64) as u64) % 2 == 1;
172
173 let table_len = self.table.len();
174
175 let index = (((rad_mod / GEN_LIMIT_F64) * (table_len as f64)) + 0.5) as usize;
177
178 let index = match is_reverse {
180 false => index,
181 true => table_len - index,
182 };
183
184 let sin = match index >= table_len {
185 false => self.table[index],
186 true => 1.0,
187 };
188
189 let is_negative = is_negative ^ (((radians / (2.0 * GEN_LIMIT_F64)) as u64) % 2 == 1);
191
192 match is_negative {
193 true => -sin,
194 false => sin,
195 }
196 }
197
198 #[inline]
199 pub fn cos(&self, radians: f64) -> f64 {
201 self.sin(radians + QUART_CIRC_F64)
202 }
203
204 #[inline]
205 pub fn tan(&self, radians: f64) -> f64 {
207 self.sin(radians) / self.cos(radians)
208 }
209}
210
211#[cfg(test)]
212mod tests {
213 use super::*;
214
215 static TABLE_F32: [f32; 1_000] = trig_table_gen_f32!(1000);
216 static TABLE_F64: [f64; 1_000_000] = trig_table_gen_f64!(1000000);
217
218 #[test]
219 fn test_sin_f32() {
220 let table = TrigTableF32::new(&TABLE_F32);
221
222 for i in 0..1000 {
225 let radians = (i as f32)/4000.0 * FULL_CIRC_F32;
227
228 let sin1 = radians.sin();
229 let sin2 = table.sin(radians);
230
231 assert_eq!(sin1, sin2, "\ni={}", i);
232 }
233
234 assert_eq!(table.sin(QUART_CIRC_F32), 1.0);
236 assert_eq!(table.sin(HALF_CIRC_F32), 0.0);
237 assert_eq!(table.sin(HALF_CIRC_F32 + QUART_CIRC_F32), -1.0);
238 assert_eq!(table.sin(FULL_CIRC_F32), 0.0);
239
240 assert_eq!(table.sin(-QUART_CIRC_F32), -1.0);
241 assert_eq!(table.sin(-HALF_CIRC_F32), 0.0);
242 assert_eq!(table.sin(-HALF_CIRC_F32 - QUART_CIRC_F32), 1.0);
243 assert_eq!(table.sin(-FULL_CIRC_F32), 0.0);
244 }
245
246 #[test]
247 fn test_sin_f64() {
248 let table = TrigTableF64::new(&TABLE_F64);
249
250 for i in 0..1_000_000 {
253 let radians = (i as f64)/4_000_000.0 * FULL_CIRC_F64;
255
256 let sin1 = radians.sin();
257 let sin2 = table.sin(radians);
258
259 assert_eq!(sin1, sin2, "\ni={}", i);
260 }
261
262 assert_eq!(table.sin(QUART_CIRC_F64), 1.0);
264 assert_eq!(table.sin(HALF_CIRC_F64), 0.0);
265 assert_eq!(table.sin(HALF_CIRC_F64 + QUART_CIRC_F64), -1.0);
266 assert_eq!(table.sin(FULL_CIRC_F64), 0.0);
267
268 assert_eq!(table.sin(-QUART_CIRC_F64), -1.0);
269 assert_eq!(table.sin(-HALF_CIRC_F64), 0.0);
270 assert_eq!(table.sin(-HALF_CIRC_F64 - QUART_CIRC_F64), 1.0);
271 assert_eq!(table.sin(-FULL_CIRC_F64), 0.0);
272 }
273}