1const fn d(bits: u64) -> f64 {
23 f64::from_bits(bits)
24}
25
26const S_ONE: f64 = 1.0;
28const S_HALF: f64 = 0.5;
29const S_SIXTH: f64 = 0.16666666666666666;
30const S_2_PI: f64 = 0.6366197723675814;
31const S_PIO2_1: f64 = 1.5707963267341256;
32const S_PIO2_1T: f64 = 6.077100506506192e-11;
33const S_PIO2_2: f64 = 6.077100506303966e-11;
34const S_PIO2_2T: f64 = 2.0222662487959506e-21;
35const S_BIG: u64 = 0x411e_8480_0000_0000; const S_S1: f64 = -0.16666666666666666;
37const S_S2: f64 = 0.008333333333333333;
38const S_S3: f64 = -0.0001984126984126984;
39const S_S4: f64 = 2.7557319223985893e-06;
40const S_C0: f64 = -0.5;
41const S_C1: f64 = 0.041666666666666664;
42const S_C2: f64 = -0.0013888888888888887;
43const S_C3: f64 = 2.4801587301587298e-05;
44const S_C4: f64 = -2.755731922398589e-07;
45
46const C_S1: f64 = -0.16666666666666666;
48const C_S2: f64 = 0.00833333333333095;
49const C_S3: f64 = -0.00019841269836761127;
50const C_S4: f64 = 2.7557316103728802e-06;
51const C_C1: f64 = 0.041666666666666664;
52const C_C2: f64 = -0.0013888888888887398;
53const C_C3: f64 = 2.480158729876704e-05;
54const C_C4: f64 = -2.755731727234419e-07;
55
56fn reduce(x: f64, ax: u64) -> (u32, f64, u64) {
59 let region = ((x * S_2_PI) + S_HALF) as i32;
60 let dr = region as f64;
61 let r1 = x - S_PIO2_1 * dr;
62 let mut r = r1 - S_PIO2_1T * dr;
63 let er = (r.to_bits() << 1) >> 53;
64 if 15 < (ax >> 52) as i64 - er as i64 {
65 let t = S_PIO2_2 * dr;
66 let r2 = r1 - t;
67 let tail = S_PIO2_2T * dr - ((r1 - r2) - t);
68 r = r2 - tail;
69 }
70 (region as u32, r, er)
71}
72
73fn sin_poly(r: f64, x2: f64, s1: f64, s2: f64, s3: f64, s4: f64) -> f64 {
74 let x4 = x2 * x2;
75 let p = (s4 * x2 + s3) * x4 + (s2 * x2 + s1);
76 p * (r * x2) + r
77}
78
79pub fn sinf(x: f32) -> f32 {
83 if x.to_bits() & 0x7f80_0000 == 0x7f80_0000 {
84 return f32::NAN;
85 }
86 let xd = x as f64;
87 let ax = xd.to_bits() & 0x7fff_ffff_ffff_ffff;
88 if ax <= 0x3fe9_21fb_5444_2d18 {
89 if ax < 0x3f80_0000_0000_0000 {
90 if ax < 0x3f20_0000_0000_0000 {
91 return x;
92 }
93 return (xd - ((xd * xd) * xd) * S_SIXTH) as f32;
94 }
95 let x2 = xd * xd;
96 return sin_poly(xd, x2, S_S1, S_S2, S_S3, S_S4) as f32;
97 }
98 let neg = xd.to_bits() != ax;
99 let v = if neg { 0.0 - xd } else { xd };
100 let (region, r, er) = if ax < S_BIG {
101 reduce(v, ax)
102 } else {
103 return (xd.sin()) as f32;
105 };
106 let odd = region & 1 != 0;
107 let mut out = if er >= 0x3f2 {
108 let x2 = r * r;
109 if odd {
110 let x4 = x2 * x2;
111 let p = ((S_C4 * x2 + S_C3) * x4 + (S_C2 * x2 + S_C1)) * x4;
112 (S_C0 * x2 + S_ONE) + p
113 } else {
114 sin_poly(r, x2, S_S1, S_S2, S_S3, S_S4)
115 }
116 } else if er > 0x3de {
117 let x2 = r * r;
118 if odd {
119 S_ONE - x2 * S_HALF
120 } else {
121 r - (S_SIXTH * r) * x2
122 }
123 } else if odd {
124 S_ONE
125 } else {
126 r
127 };
128 let q = (region >> 1) & 1;
129 if q != neg as u32 {
130 out = 0.0 - out;
131 }
132 out as f32
133}
134
135pub fn cosf(x: f32) -> f32 {
139 if x.to_bits() & 0x7f80_0000 == 0x7f80_0000 {
140 return f32::NAN;
141 }
142 let xd = x as f64;
143 let ax = xd.to_bits() & 0x7fff_ffff_ffff_ffff;
144 let (region, r, er) = if ax <= 0x3fe9_21fb_5444_2d18 {
145 (0, xd, 0x3f2)
146 } else {
147 let v = if xd.to_bits() != ax { 0.0 - xd } else { xd };
148 if ax < S_BIG {
149 reduce(v, ax)
150 } else {
151 return (xd.cos()) as f32;
153 }
154 };
155 let odd = region & 1 != 0;
156 let x2 = r * r;
157 let mut out = if er >= 0x3f2 {
158 let x4 = x2 * x2;
159 if odd {
160 ((C_S4 * x2 + C_S3) * x4 + (C_S2 * x2 + C_S1)) * (x2 * r) + r
161 } else {
162 let p = ((C_C4 * x2 + C_C3) * x4 + (C_C2 * x2 + C_C1)) * x4;
163 p - (S_HALF * x2 - S_ONE)
164 }
165 } else if er > 0x3de {
166 if odd {
167 r - (S_SIXTH * r) * x2
168 } else {
169 S_ONE - x2 * S_HALF
170 }
171 } else if odd {
172 r
173 } else {
174 S_ONE
175 };
176 if (region.wrapping_add(1)) & 2 != 0 {
177 out = 0.0 - out;
178 }
179 out as f32
180}
181
182pub fn acosf(x: f32) -> f32 {
186 const P0: f32 = -0.013381929136812687;
187 const P1: f32 = 0.003961374517530203;
188 const P2: f32 = 0.05652986839413643;
189 const P3: f32 = 0.18416160345077515;
190 const Q0: f32 = 1.1049696207046509;
191 const Q1: f32 = 0.8364112973213196;
192 const PIO2_F: f32 = 1.5707963705062866;
193 const PI_F: f32 = 3.1415927410125732;
194 const PIO2_LO: f64 = 6.123233995736766e-17;
195 const PIO2: f64 = 1.5707963267948966;
196 const PI: f64 = 3.141592653589793;
197 let ux = x.to_bits();
198 let negative = ux & 0x8000_0000 != 0;
199 let e = ((ux >> 23) & 0xff) as i32 - 127;
200 if ux & 0x7fff_ffff > 0x7f80_0000 {
201 return f32::from_bits(ux | 0x0040_0000);
202 }
203 if e < -26 {
204 return PIO2_F;
205 }
206 if 0 <= e {
207 return if x == 1.0 {
208 0.0
209 } else if x == -1.0 {
210 PI_F
211 } else {
212 f32::NAN
213 };
214 }
215 let mut ax = if negative { -x } else { x };
216 let transform = -1 <= e;
217 let (r, s) = if transform {
218 let r = (1.0 - ax) * 0.5;
219 let s = r.sqrt();
220 ax = s;
221 (r, s)
222 } else {
223 (ax * ax, 0.0)
224 };
225 let mut p = P0 - r * P1;
226 p *= r;
227 p -= P2;
228 p *= r;
229 p += P3;
230 p *= r;
231 let q = Q0 - r * Q1;
232 let u = p / q;
233 if transform {
234 if negative {
235 let t = (((u * ax) as f64 - PIO2_LO) + s as f64) * 2.0;
236 (PI - t) as f32
237 } else {
238 let c = f32::from_bits(s.to_bits() & 0xffff_0000);
239 let a = (2.0 * ax) * u;
240 let b = ((r - c * c) / (c + s)) * 2.0;
241 b + a + c * 2.0
242 }
243 } else {
244 let t = (x as f64) - (PIO2_LO - (u * x) as f64);
245 (PIO2 - t) as f32
246 }
247}
248
249pub fn atan2f(y: f32, x: f32) -> f32 {
254 const SIGN: u64 = 0x8000_0000_0000_0000;
255 const ABS: u64 = 0x7fff_ffff_ffff_ffff;
256 const INF: u64 = 0x7ff0_0000_0000_0000;
257 const PI: f64 = 3.141592653589793;
258 const PIO2: f64 = 1.5707963267948966;
259 const PIO4: f64 = 0.7853981633974483;
260 const PI3O4: f64 = 2.356194490192345;
261 let (yd, xd) = (y as f64, x as f64);
262 let (yb, xb) = (yd.to_bits(), xd.to_bits());
263 let (ysign, xsign) = (yb & SIGN != 0, xb & SIGN != 0);
264 let (ay, ax) = (yb & ABS, xb & ABS);
265 let diff = ((yb >> 52) & 0x7ff) as i32 - ((xb >> 52) & 0x7ff) as i32;
266 let signed = |v: f64, neg: bool| (if neg { -v } else { v }) as f32;
267 if ax > INF {
268 return f32::from_bits(x.to_bits() | 0x0040_0000);
269 }
270 if ay > INF {
271 return f32::from_bits(y.to_bits() | 0x0040_0000);
272 }
273 if ay == 0 {
274 return if xsign { signed(PI, ysign) } else { yd as f32 };
275 }
276 if ax == 0 && ysign {
277 return signed(PIO2, true);
278 }
279 if 26 < diff {
280 return signed(PIO2, ysign);
281 }
282 if diff < -13 && !xsign {
283 if diff < -150 {
284 return if ysign { -0.0 } else { 0.0 };
285 }
286 if diff >= -126 {
287 return (yd / xd) as f32;
288 }
289 let f = (d(0x4630_0000_0000_0000) * yd) / xd;
291 let sign = f.to_bits() & SIGN;
292 let a = f.to_bits() & ABS;
293 let e = ((a >> 52) & 0x7ff) as i32 - 100;
294 let bits = if 0 < e {
295 ((e as u64) << 52) | (a & 0x800f_ffff_ffff_ffff)
296 } else {
297 let m = (a & 0x801f_ffff_ffff_ffff) | 0x0010_0000_0000_0000;
298 let n = 1 - e;
299 if n <= 0x36 {
300 let m = m >> (n - 1);
301 (m >> 1) + (m & 1)
302 } else {
303 0
304 }
305 };
306 return f64::from_bits(bits | sign) as f32;
307 }
308 if diff < -26 && xsign {
309 return signed(PI, ysign);
310 }
311 if ay == INF && ax == INF {
312 return signed(if xsign { PI3O4 } else { PIO4 }, ysign);
313 }
314 let (mut small, mut big) = (f64::from_bits(ay), f64::from_bits(ax));
315 let swap = small > big;
316 if swap {
317 std::mem::swap(&mut small, &mut big);
318 }
319 let t = small / big;
320 let mut r = if t > 0.0625 {
321 let u = (t * 256.0 + 0.5) as i32;
322 let j = u as f64;
323 let dd = (small * 256.0 - j * big) / (j * small + big * 256.0);
324 let a = dd + d(ATAN_TABLE[(u - 16) as usize]);
325 a - ((dd * dd) * dd) * 0.33333333333224097
326 } else if 0.0001 > t {
327 t
328 } else {
329 let x2 = t * t;
330 let p = 0.3333333333333317 - (0.19999999999393223 - x2 * 0.1428571356180717) * x2;
331 t - p * (x2 * t)
332 };
333 if swap {
334 r = PIO2 - r;
335 }
336 if xsign {
337 r = PI - r;
338 }
339 if ysign {
340 r = -r;
341 }
342 r as f32
343}
344
345const ATAN_TABLE: [u64; 241] = [
348 0x3faff55bb72cfde9,
349 0x3fb0f99ea71d52a6,
350 0x3fb1f86dbf082d58,
351 0x3fb2f719318a4a9a,
352 0x3fb3f59f0e7c559d,
353 0x3fb4f3fd677292fb,
354 0x3fb5f2324fd2d7b2,
355 0x3fb6f03bdcea4b0c,
356 0x3fb7ee182602f10e,
357 0x3fb8ebc54478fb28,
358 0x3fb9e94153cfdcf1,
359 0x3fbae68a71c722b8,
360 0x3fbbe39ebe6f07c3,
361 0x3fbce07c5c3cca32,
362 0x3fbddd21701eba6e,
363 0x3fbed98c2190043a,
364 0x3fbfd5ba9aac2f6d,
365 0x3fc068d584212b3d,
366 0x3fc0e6adccf40881,
367 0x3fc1646541060850,
368 0x3fc1e1fafb043726,
369 0x3fc25f6e171a535c,
370 0x3fc2dcbdb2fba1ff,
371 0x3fc359e8edeb99a3,
372 0x3fc3d6eee8c6626c,
373 0x3fc453cec6092a9e,
374 0x3fc4d087a9da4f17,
375 0x3fc54d18ba11570a,
376 0x3fc5c9811e3ec269,
377 0x3fc645bfffb3aa73,
378 0x3fc6c1d4898933d8,
379 0x3fc73dbde8a7d201,
380 0x3fc7b97b4bce5b02,
381 0x3fc8350be398ebc7,
382 0x3fc8b06ee2879c28,
383 0x3fc92ba37d050271,
384 0x3fc9a6a8e96c8626,
385 0x3fca217e601081a5,
386 0x3fca9c231b403279,
387 0x3fcb1696574d780b,
388 0x3fcb90d7529260a2,
389 0x3fcc0ae54d768466,
390 0x3fcc84bf8a742e6d,
391 0x3fccfe654e1d5395,
392 0x3fcd77d5df205736,
393 0x3fcdf110864c9d9d,
394 0x3fce6a148e96ec4d,
395 0x3fcee2e1451d980c,
396 0x3fcf5b75f92c80dd,
397 0x3fcfd3d1fc40dbe4,
398 0x3fd025fa510665b5,
399 0x3fd061eea03d6290,
400 0x3fd09dc597d86362,
401 0x3fd0d97ee509acb3,
402 0x3fd1151a362431c9,
403 0x3fd150973a9ce546,
404 0x3fd18bf5a30bf178,
405 0x3fd1c735212dd883,
406 0x3fd2025567e47c95,
407 0x3fd23d562b381041,
408 0x3fd278372057ef45,
409 0x3fd2b2f7fd9b5fe2,
410 0x3fd2ed987a823cfe,
411 0x3fd328184fb58951,
412 0x3fd362773707ebcb,
413 0x3fd39cb4eb76157b,
414 0x3fd3d6d129271134,
415 0x3fd410cbad6c7d32,
416 0x3fd44aa436c2af09,
417 0x3fd4845a84d0c21b,
418 0x3fd4bdee586890e6,
419 0x3fd4f75f73869978,
420 0x3fd530ad9951cd49,
421 0x3fd569d88e1b4cd7,
422 0x3fd5a2e0175e0f4e,
423 0x3fd5dbc3fbbe768d,
424 0x3fd614840309cfe1,
425 0x3fd64d1ff635c1c5,
426 0x3fd685979f5fa6fd,
427 0x3fd6bdeac9cbd76c,
428 0x3fd6f61941e4def0,
429 0x3fd72e22d53aa2a9,
430 0x3fd7660752817501,
431 0x3fd79dc6899118d1,
432 0x3fd7d5604b63b3f7,
433 0x3fd80cd46a14b1d0,
434 0x3fd84422b8df95d7,
435 0x3fd87b4b0c1ebedb,
436 0x3fd8b24d394a1b25,
437 0x3fd8e92916f5cde8,
438 0x3fd91fde7cd0c662,
439 0x3fd9566d43a34907,
440 0x3fd98cd5454d6b18,
441 0x3fd9c3165cc58107,
442 0x3fd9f93066168001,
443 0x3fda2f233e5e530b,
444 0x3fda64eec3cc23fc,
445 0x3fda9a92d59e98cf,
446 0x3fdad00f5422058b,
447 0x3fdb056420ae9343,
448 0x3fdb3a911da65c6c,
449 0x3fdb6f962e737efb,
450 0x3fdba473378624a5,
451 0x3fdbd9281e528191,
452 0x3fdc0db4c94ec9ef,
453 0x3fdc42191ff11eb6,
454 0x3fdc76550aad71f8,
455 0x3fdcaa6872f3631b,
456 0x3fdcde53432c1350,
457 0x3fdd121566b7f2ad,
458 0x3fdd45aec9ec862b,
459 0x3fdd791f5a1226f4,
460 0x3fddac670561bb4f,
461 0x3fdddf85bb026974,
462 0x3fde127b6b0744af,
463 0x3fde4548066cf51a,
464 0x3fde77eb7f175a34,
465 0x3fdeaa65c7cf28c4,
466 0x3fdedcb6d43f8434,
467 0x3fdf0ede98f393cf,
468 0x3fdf40dd0b541417,
469 0x3fdf72b221a4e495,
470 0x3fdfa45dd3029258,
471 0x3fdfd5e0175fdf83,
472 0x3fe0039c73c1a40b,
473 0x3fe01c341e82422d,
474 0x3fe034b709250488,
475 0x3fe04d25314342e5,
476 0x3fe0657e94db30cf,
477 0x3fe07dc3324e9b38,
478 0x3fe095f30861a58f,
479 0x3fe0ae0e1639866c,
480 0x3fe0c6145b5b43da,
481 0x3fe0de05d7aa6f7c,
482 0x3fe0f5e28b67e295,
483 0x3fe10daa77307a0d,
484 0x3fe1255d9bfbd2a8,
485 0x3fe13cfbfb1b056e,
486 0x3fe1548596376469,
487 0x3fe16bfa6f5137e1,
488 0x3fe1835a88be7c13,
489 0x3fe19aa5e5299f99,
490 0x3fe1b1dc87904284,
491 0x3fe1c8fe7341f64f,
492 0x3fe1e00babdefeb3,
493 0x3fe1f7043557138a,
494 0x3fe20de813e823b1,
495 0x3fe224b74c1d192a,
496 0x3fe23b71e2cc9e6a,
497 0x3fe25217dd17e501,
498 0x3fe268a940696da6,
499 0x3fe27f261273d1b3,
500 0x3fe2958e59308e30,
501 0x3fe2abe21aded073,
502 0x3fe2c2215e024465,
503 0x3fe2d84c2961e48b,
504 0x3fe2ee628406cbca,
505 0x3fe30464753b090a,
506 0x3fe31a52048874be,
507 0x3fe3302b39b78856,
508 0x3fe345f01cce37bb,
509 0x3fe35ba0b60eccce,
510 0x3fe3713d0df6c503,
511 0x3fe386c52d3db11e,
512 0x3fe39c391cd41719,
513 0x3fe3b198e5e2564a,
514 0x3fe3c6e491c78dc4,
515 0x3fe3dc1c2a188504,
516 0x3fe3f13fb89e96f4,
517 0x3fe4064f47569f48,
518 0x3fe41b4ae06fea41,
519 0x3fe430328e4b26d5,
520 0x3fe445065b795b55,
521 0x3fe459c652badc7f,
522 0x3fe46e727efe4715,
523 0x3fe4830aeb5f7bfd,
524 0x3fe4978fa3269ee1,
525 0x3fe4ac00b1c71762,
526 0x3fe4c05e22de94e4,
527 0x3fe4d4a8023414e8,
528 0x3fe4e8de5bb6ec04,
529 0x3fe4fd013b7dd17e,
530 0x3fe51110adc5ed81,
531 0x3fe5250cbef1e9fa,
532 0x3fe538f57b89061e,
533 0x3fe54ccaf0362c8f,
534 0x3fe5608d29c70c34,
535 0x3fe5743c352b33b9,
536 0x3fe587d81f732fba,
537 0x3fe59b60f5cfab9d,
538 0x3fe5aed6c5909517,
539 0x3fe5c2399c244260,
540 0x3fe5d58987169b18,
541 0x3fe5e8c6941043cf,
542 0x3fe5fbf0d0d5cc49,
543 0x3fe60f084b46e05e,
544 0x3fe6220d115d7b8d,
545 0x3fe634ff312d1f3b,
546 0x3fe647deb8e20b8f,
547 0x3fe65aabb6c07b02,
548 0x3fe66d663923e086,
549 0x3fe6800e4e7e2857,
550 0x3fe692a40556fb6a,
551 0x3fe6a5276c4b0575,
552 0x3fe6b798920b3d98,
553 0x3fe6c9f7855c3198,
554 0x3fe6dc44551553ae,
555 0x3fe6ee7f10204aef,
556 0x3fe700a7c5784633,
557 0x3fe712be84295198,
558 0x3fe724c35b4fae7b,
559 0x3fe736b65a172dff,
560 0x3fe748978fba8e0f,
561 0x3fe75a670b82d8d8,
562 0x3fe76c24dcc6c6c0,
563 0x3fe77dd112ea22c7,
564 0x3fe78f6bbd5d315e,
565 0x3fe7a0f4eb9c19a2,
566 0x3fe7b26cad2e50fd,
567 0x3fe7c3d311a6092b,
568 0x3fe7d528289fa093,
569 0x3fe7e66c01c114fd,
570 0x3fe7f79eacb97898,
571 0x3fe808c03940694a,
572 0x3fe819d0b7158a4c,
573 0x3fe82ad036000005,
574 0x3fe83bbec5cdee22,
575 0x3fe84c9c7653f7ea,
576 0x3fe85d69576cc2c5,
577 0x3fe86e2578f87ae5,
578 0x3fe87ed0eadc5a2a,
579 0x3fe88f6bbd023118,
580 0x3fe89ff5ff57f1f7,
581 0x3fe8b06fc1cf3dfe,
582 0x3fe8c0d9145cf49d,
583 0x3fe8d13206f8c4ca,
584 0x3fe8e17aa99cc05d,
585 0x3fe8f1b30c44f167,
586 0x3fe901db3eeef187,
587 0x3fe911f35199833b,
588 0x3fe921fb54442d18,
589];
590
591#[cfg(test)]
592mod tests {
593 use super::*;
594
595 #[test]
597 fn the_atan_table_is_the_runtimes() {
598 let path = concat!(
599 env!("CARGO_MANIFEST_DIR"),
600 "/../../game-data/original/Runtime-steam-x64.dll"
601 );
602 let Ok(img) = std::fs::read(path) else {
603 return;
604 };
605 let va = 0x1801724a0u64 - 0x180000000;
607 let pe = u32::from_le_bytes(img[0x3c..0x40].try_into().unwrap()) as usize;
608 let n = u16::from_le_bytes(img[pe + 6..pe + 8].try_into().unwrap()) as usize;
609 let opt = u16::from_le_bytes(img[pe + 20..pe + 22].try_into().unwrap()) as usize;
610 let mut off = None;
611 for k in 0..n {
612 let o = pe + 24 + opt + 40 * k;
613 let rd =
614 |i: usize| u32::from_le_bytes(img[o + i..o + i + 4].try_into().unwrap()) as u64;
615 let (vsize, rva, raw) = (rd(8), rd(12), rd(20));
616 if rva <= va && va < rva + vsize {
617 off = Some((va - rva + raw) as usize);
618 }
619 }
620 let off = off.unwrap();
621 for (k, &want) in ATAN_TABLE.iter().enumerate() {
622 let got = u64::from_le_bytes(img[off + 8 * k..off + 8 * k + 8].try_into().unwrap());
623 assert_eq!(got, want, "entry {k}");
624 }
625 }
626
627 #[test]
628 fn plain_values() {
629 assert_eq!(sinf(0.0), 0.0);
630 assert_eq!(cosf(0.0), 1.0);
631 assert_eq!(acosf(1.0), 0.0);
632 assert!((atan2f(1.0, 1.0) - std::f32::consts::FRAC_PI_4).abs() < 1e-7);
633 }
634}