BEEP-8 Helper Lib 1.0.0
Loading...
Searching...
No Matches
fxmath.h
1#pragma once
2/*
3MIT License
4
5Copyright (c) 2019 Mike Lankamp
6https://github.com/MikeLankamp/fpm
7
8Permission is hereby granted, free of charge, to any person obtaining a copy
9of this software and associated documentation files (the "Software"), to deal
10in the Software without restriction, including without limitation the rights
11to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12copies of the Software, and to permit persons to whom the Software is
13furnished to do so, subject to the following conditions:
14
15The above copyright notice and this permission notice shall be included in all
16copies or substantial portions of the Software.
17
18THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24SOFTWARE.
25*/
26#include <fixed.h>
27#include <cmath>
28
29namespace fpm
30{
31
32//
33// Helper functions
34//
35namespace detail
36{
37
38// Returns the index of the most-signifcant set bit
39inline long find_highest_bit(unsigned long long value) noexcept
40{
41 assert(value != 0);
42#if defined(_MSC_VER)
43 unsigned long index;
44#if defined(_WIN64)
45 _BitScanReverse64(&index, value);
46#else
47 if (_BitScanReverse(&index, static_cast<unsigned long>(value >> 32)) != 0) {
48 index += 32;
49 } else {
50 _BitScanReverse(&index, static_cast<unsigned long>(value & 0xfffffffflu));
51 }
52#endif
53 return index;
54#elif defined(__GNUC__) || defined(__clang__)
55 return sizeof(value) * 8 - 1 - __builtin_clzll(value);
56#else
57# error "your platform does not support find_highest_bit()"
58#endif
59}
60
61}
62
63//
64// Classification methods
65//
66
67template <typename B, typename I, unsigned int F>
68constexpr inline int fpclassify(fixed<B, I, F> x) noexcept
69{
70 return (x.raw_value() == 0) ? FP_ZERO : FP_NORMAL;
71}
72
73template <typename B, typename I, unsigned int F>
74constexpr inline bool isfinite(fixed<B, I, F>) noexcept
75{
76 return true;
77}
78
79template <typename B, typename I, unsigned int F>
80constexpr inline bool isinf(fixed<B, I, F>) noexcept
81{
82 return false;
83}
84
85template <typename B, typename I, unsigned int F>
86constexpr inline bool isnan(fixed<B, I, F>) noexcept
87{
88 return false;
89}
90
91template <typename B, typename I, unsigned int F>
92constexpr inline bool isnormal(fixed<B, I, F>) noexcept
93{
94 return true;
95}
96
97template <typename B, typename I, unsigned int F>
98constexpr inline bool signbit(fixed<B, I, F> x) noexcept
99{
100 return x.raw_value() < 0;
101}
102
103template <typename B, typename I, unsigned int F>
104constexpr inline bool isgreater(fixed<B, I, F> x, fixed<B, I, F> y) noexcept
105{
106 return x > y;
107}
108
109template <typename B, typename I, unsigned int F>
110constexpr inline bool isgreaterequal(fixed<B, I, F> x, fixed<B, I, F> y) noexcept
111{
112 return x >= y;
113}
114
115template <typename B, typename I, unsigned int F>
116constexpr inline bool isless(fixed<B, I, F> x, fixed<B, I, F> y) noexcept
117{
118 return x < y;
119}
120
121template <typename B, typename I, unsigned int F>
122constexpr inline bool islessequal(fixed<B, I, F> x, fixed<B, I, F> y) noexcept
123{
124 return x <= y;
125}
126
127template <typename B, typename I, unsigned int F>
128constexpr inline bool islessgreater(fixed<B, I, F> x, fixed<B, I, F> y) noexcept
129{
130 return x != y;
131}
132
133template <typename B, typename I, unsigned int F>
134constexpr inline bool isunordered(fixed<B, I, F> x, fixed<B, I, F> y) noexcept
135{
136 return false;
137}
138
139//
140// Nearest integer operations
141//
142template <typename B, typename I, unsigned int F>
143inline fixed<B, I, F> ceil(fixed<B, I, F> x) noexcept
144{
145 constexpr auto FRAC = B(1) << F;
146 auto value = x.raw_value();
147 if (value > 0) value += FRAC - 1;
148 return fixed<B, I, F>::from_raw_value(value / FRAC * FRAC);
149}
150
151template <typename B, typename I, unsigned int F>
152inline fixed<B, I, F> floor(fixed<B, I, F> x) noexcept
153{
154 constexpr auto FRAC = B(1) << F;
155 auto value = x.raw_value();
156 if (value < 0) value -= FRAC - 1;
157 return fixed<B, I, F>::from_raw_value(value / FRAC * FRAC);
158}
159
160template <typename B, typename I, unsigned int F>
161inline fixed<B, I, F> trunc(fixed<B, I, F> x) noexcept
162{
163 constexpr auto FRAC = B(1) << F;
164 return fixed<B, I, F>::from_raw_value(x.raw_value() / FRAC * FRAC);
165}
166
167template <typename B, typename I, unsigned int F>
168inline fixed<B, I, F> round(fixed<B, I, F> x) noexcept
169{
170 constexpr auto FRAC = B(1) << F;
171 auto value = x.raw_value() / (FRAC / 2);
172 return fixed<B, I, F>::from_raw_value(((value / 2) + (value % 2)) * FRAC);
173}
174
175template <typename B, typename I, unsigned int F>
176fixed<B, I, F> nearbyint(fixed<B, I, F> x) noexcept
177{
178 // Rounding mode is assumed to be FE_TONEAREST
179 constexpr auto FRAC = B(1) << F;
180 auto value = x.raw_value();
181 const bool is_half = std::abs(value % FRAC) == FRAC / 2;
182 value /= FRAC / 2;
183 value = (value / 2) + (value % 2);
184 value -= (value % 2) * is_half;
185 return fixed<B, I, F>::from_raw_value(value * FRAC);
186}
187
188template <typename B, typename I, unsigned int F>
189constexpr inline fixed<B, I, F> rint(fixed<B, I, F> x) noexcept
190{
191 // Rounding mode is assumed to be FE_TONEAREST
192 return nearbyint(x);
193}
194
195//
196// Mathematical functions
197//
198template <typename B, typename I, unsigned int F>
199constexpr inline fixed<B, I, F> abs(fixed<B, I, F> x) noexcept
200{
201 return (x >= fixed<B, I, F>{0}) ? x : -x;
202}
203
204template <typename B, typename I, unsigned int F>
205constexpr inline fixed<B, I, F> fmod(fixed<B, I, F> x, fixed<B, I, F> y) noexcept
206{
207 return
208 assert(y.raw_value() != 0),
209 fixed<B, I, F>::from_raw_value(x.raw_value() % y.raw_value());
210}
211
212template <typename B, typename I, unsigned int F>
213constexpr inline fixed<B, I, F> remainder(fixed<B, I, F> x, fixed<B, I, F> y) noexcept
214{
215 return
216 assert(y.raw_value() != 0),
217 x - nearbyint(x / y) * y;
218}
219
220template <typename B, typename I, unsigned int F>
221inline fixed<B, I, F> remquo(fixed<B, I, F> x, fixed<B, I, F> y, int* quo) noexcept
222{
223 assert(y.raw_value() != 0);
224 assert(quo != nullptr);
225 *quo = x.raw_value() / y.raw_value();
226 return fixed<B, I, F>::from_raw_value(x.raw_value() % y.raw_value());
227}
228
229//
230// Manipulation functions
231//
232
233template <typename B, typename I, unsigned int F, typename C, typename J, unsigned int G>
234constexpr inline fixed<B, I, F> copysign(fixed<B, I, F> x, fixed<C, J, G> y) noexcept
235{
236 return
237 x = abs(x),
238 (y >= fixed<C, J, G>{0}) ? x : -x;
239}
240
241template <typename B, typename I, unsigned int F>
242constexpr inline fixed<B, I, F> nextafter(fixed<B, I, F> from, fixed<B, I, F> to) noexcept
243{
244 return from == to ? to :
245 to > from ? fixed<B, I, F>::from_raw_value(from.raw_value() + 1)
246 : fixed<B, I, F>::from_raw_value(from.raw_value() - 1);
247}
248
249template <typename B, typename I, unsigned int F>
250constexpr inline fixed<B, I, F> nexttoward(fixed<B, I, F> from, fixed<B, I, F> to) noexcept
251{
252 return nextafter(from, to);
253}
254
255template <typename B, typename I, unsigned int F>
256inline fixed<B, I, F> modf(fixed<B, I, F> x, fixed<B, I, F>* iptr) noexcept
257{
258 const auto raw = x.raw_value();
259 constexpr auto FRAC = B{1} << F;
260 *iptr = fixed<B, I, F>::from_raw_value(raw / FRAC * FRAC);
261 return fixed<B, I, F>::from_raw_value(raw % FRAC);
262}
263
264
265//
266// Power functions
267//
268
269template <typename B, typename I, unsigned int F, typename T, typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
270fixed<B, I, F> pow(fixed<B, I, F> base, T exp) noexcept
271{
272 using Fixed = fixed<B, I, F>;
273 constexpr auto FRAC = B(1) << F;
274
275 if (base == Fixed(0)) {
276 assert(exp > 0);
277 return Fixed(0);
278 }
279
280 Fixed result {1};
281 if (exp < 0)
282 {
283 for (Fixed intermediate = base; exp != 0; exp /= 2, intermediate *= intermediate)
284 {
285 if ((exp % 2) != 0)
286 {
287 result /= intermediate;
288 }
289 }
290 }
291 else
292 {
293 for (Fixed intermediate = base; exp != 0; exp /= 2, intermediate *= intermediate)
294 {
295 if ((exp % 2) != 0)
296 {
297 result *= intermediate;
298 }
299 }
300 }
301 return result;
302}
303
304template <typename B, typename I, unsigned int F>
305fixed<B, I, F> pow(fixed<B, I, F> base, fixed<B, I, F> exp) noexcept
306{
307 using Fixed = fixed<B, I, F>;
308
309 if (base == Fixed(0)) {
310 assert(exp > Fixed(0));
311 return Fixed(0);
312 }
313
314 if (exp < Fixed(0))
315 {
316 return 1 / pow(base, -exp);
317 }
318
319 constexpr auto FRAC = B(1) << F;
320 if (exp.raw_value() % FRAC == 0)
321 {
322 // Non-fractional exponents are easier to calculate
323 return pow(base, exp.raw_value() / FRAC);
324 }
325
326 // For negative bases we do not support fractional exponents.
327 // Technically fractions with odd denominators could work,
328 // but that's too much work to figure out.
329 assert(base > Fixed(0));
330 return exp2(log2(base) * exp);
331}
332
333template <typename B, typename I, unsigned int F>
334fixed<B, I, F> exp(fixed<B, I, F> x) noexcept
335{
336 using Fixed = fixed<B, I, F>;
337 if (x < Fixed(0)) {
338 return 1 / exp(-x);
339 }
340 constexpr auto FRAC = B(1) << F;
341 const B x_int = x.raw_value() / FRAC;
342 x -= x_int;
343 assert(x >= Fixed(0) && x < Fixed(1));
344
345 constexpr auto fA = Fixed::template from_fixed_point<63>( 128239257017632854ll); // 1.3903728105644451e-2
346 constexpr auto fB = Fixed::template from_fixed_point<63>( 320978614890280666ll); // 3.4800571158543038e-2
347 constexpr auto fC = Fixed::template from_fixed_point<63>(1571680799599592947ll); // 1.7040197373796334e-1
348 constexpr auto fD = Fixed::template from_fixed_point<63>(4603349000587966862ll); // 4.9909609871464493e-1
349 constexpr auto fE = Fixed::template from_fixed_point<62>(4612052447974689712ll); // 1.0000794567422495
350 constexpr auto fF = Fixed::template from_fixed_point<63>(9223361618412247875ll); // 9.9999887043019773e-1
351 return pow(Fixed::e(), x_int) * (((((fA * x + fB) * x + fC) * x + fD) * x + fE) * x + fF);
352}
353
354template <typename B, typename I, unsigned int F>
355fixed<B, I, F> exp2(fixed<B, I, F> x) noexcept
356{
357 using Fixed = fixed<B, I, F>;
358 if (x < Fixed(0)) {
359 return 1 / exp2(-x);
360 }
361 constexpr auto FRAC = B(1) << F;
362 const B x_int = x.raw_value() / FRAC;
363 x -= x_int;
364 assert(x >= Fixed(0) && x < Fixed(1));
365
366 constexpr auto fA = Fixed::template from_fixed_point<63>( 17491766697771214ll); // 1.8964611454333148e-3
367 constexpr auto fB = Fixed::template from_fixed_point<63>( 82483038782406547ll); // 8.9428289841091295e-3
368 constexpr auto fC = Fixed::template from_fixed_point<63>( 515275173969157690ll); // 5.5866246304520701e-2
369 constexpr auto fD = Fixed::template from_fixed_point<63>(2214897896212987987ll); // 2.4013971109076949e-1
370 constexpr auto fE = Fixed::template from_fixed_point<63>(6393224161192452326ll); // 6.9315475247516736e-1
371 constexpr auto fF = Fixed::template from_fixed_point<63>(9223371050976163566ll); // 9.9999989311082668e-1
372 return Fixed(1 << x_int) * (((((fA * x + fB) * x + fC) * x + fD) * x + fE) * x + fF);
373}
374
375template <typename B, typename I, unsigned int F>
376fixed<B, I, F> expm1(fixed<B, I, F> x) noexcept
377{
378 return exp(x) - 1;
379}
380
381template <typename B, typename I, unsigned int F>
382fixed<B, I, F> log2(fixed<B, I, F> x) noexcept
383{
384 using Fixed = fixed<B, I, F>;
385 assert(x > Fixed(0));
386
387 // Normalize input to the [1:2] domain
388 B value = x.raw_value();
389 const long highest = detail::find_highest_bit(value);
390 if (highest >= F) {
391 value >>= (highest - F);
392 } else {
393 value <<= (F - highest);
394 }
395 x = Fixed::from_raw_value(value);
396 assert(x >= Fixed(1) && x < Fixed(2));
397
398 constexpr auto fA = Fixed::template from_fixed_point<63>( 413886001457275979ll); // 4.4873610194131727e-2
399 constexpr auto fB = Fixed::template from_fixed_point<63>(-3842121857793256941ll); // -4.1656368651734915e-1
400 constexpr auto fC = Fixed::template from_fixed_point<62>( 7522345947206307744ll); // 1.6311487636297217
401 constexpr auto fD = Fixed::template from_fixed_point<61>(-8187571043052183818ll); // -3.5507929249026341
402 constexpr auto fE = Fixed::template from_fixed_point<60>( 5870342889289496598ll); // 5.0917108110420042
403 constexpr auto fF = Fixed::template from_fixed_point<61>(-6457199832668582866ll); // -2.8003640347009253
404 return Fixed(highest - F) + (((((fA * x + fB) * x + fC) * x + fD) * x + fE) * x + fF);
405}
406
407template <typename B, typename I, unsigned int F>
408fixed<B, I, F> log(fixed<B, I, F> x) noexcept
409{
410 using Fixed = fixed<B, I, F>;
411 return log2(x) / log2(Fixed::e());
412}
413
414template <typename B, typename I, unsigned int F>
415fixed<B, I, F> log10(fixed<B, I, F> x) noexcept
416{
417 using Fixed = fixed<B, I, F>;
418 return log2(x) / log2(Fixed(10));
419}
420
421template <typename B, typename I, unsigned int F>
422fixed<B, I, F> log1p(fixed<B, I, F> x) noexcept
423{
424 return log(1 + x);
425}
426
427template <typename B, typename I, unsigned int F>
428fixed<B, I, F> cbrt(fixed<B, I, F> x) noexcept
429{
430 using Fixed = fixed<B, I, F>;
431
432 if (x == Fixed(0))
433 {
434 return x;
435 }
436 if (x < Fixed(0))
437 {
438 return -cbrt(-x);
439 }
440 assert(x >= Fixed(0));
441
442 // Finding the cube root of an integer, taken from Hacker's Delight,
443 // based on the square root algorithm.
444
445 // We start at the greatest power of eight that's less than the argument.
446 int ofs = ((detail::find_highest_bit(x.raw_value()) + 2*F) / 3 * 3);
447 I num = I{x.raw_value()};
448 I res = 0;
449
450 const auto do_round = [&]
451 {
452 for (; ofs >= 0; ofs -= 3)
453 {
454 res += res;
455 const I val = (3*res*(res + 1) + 1) << ofs;
456 if (num >= val)
457 {
458 num -= val;
459 res++;
460 }
461 }
462 };
463
464 // We should shift by 2*F (since there are two multiplications), but that
465 // could overflow even the intermediate type, so we have to split the
466 // algorithm up in two rounds of F bits each. Each round will deplete
467 // 'num' digit by digit, so after a round we can shift it again.
468 num <<= F;
469 ofs -= F;
470 do_round();
471
472 num <<= F;
473 ofs += F;
474 do_round();
475
476 return Fixed::from_raw_value(static_cast<B>(res));
477}
478
479template <typename B, typename I, unsigned int F>
480fixed<B, I, F> sqrt(fixed<B, I, F> x) noexcept
481{
482 using Fixed = fixed<B, I, F>;
483
484 if (x == Fixed(0))
485 {
486 return x;
487 }
488
489 // Finding the square root of an integer in base-2, from:
490 // https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Binary_numeral_system_.28base_2.29
491
492 // Shift by F first because it's fixed-point.
493 I num = I{x.raw_value()} << F;
494 I res = 0;
495
496 // "bit" starts at the greatest power of four that's less than the argument.
497 for (I bit = I{1} << ((detail::find_highest_bit(x.raw_value()) + F) / 2 * 2); bit != 0; bit >>= 2)
498 {
499 const I val = res + bit;
500 res >>= 1;
501 if (num >= val)
502 {
503 num -= val;
504 res += bit;
505 }
506 }
507
508 // Round the last digit up if necessary
509 if (num > res)
510 {
511 res++;
512 }
513
514 return Fixed::from_raw_value(static_cast<B>(res));
515}
516
517template <typename B, typename I, unsigned int F>
518fixed<B, I, F> hypot(fixed<B, I, F> x, fixed<B, I, F> y) noexcept
519{
520 assert(x != 0 || y != 0);
521 return sqrt(x*x + y*y);
522}
523
524//
525// Trigonometry functions
526//
527
528template <typename B, typename I, unsigned int F>
529fixed<B, I, F> sin(fixed<B, I, F> x) noexcept
530{
531 // This sine uses a fifth-order curve-fitting approximation originally
532 // described by Jasper Vijn on coranac.com which has a worst-case
533 // relative error of 0.07% (over [-pi:pi]).
534 using Fixed = fixed<B, I, F>;
535
536 // Turn x from [0..2*PI] domain into [0..4] domain
537 x = fmod(x, Fixed::two_pi());
538 x = x / Fixed::half_pi();
539
540 // Take x modulo one rotation, so [-4..+4].
541 if (x < Fixed(0)) {
542 x += Fixed(4);
543 }
544
545 int sign = +1;
546 if (x > Fixed(2)) {
547 // Reduce domain to [0..2].
548 sign = -1;
549 x -= Fixed(2);
550 }
551
552 if (x > Fixed(1)) {
553 // Reduce domain to [0..1].
554 x = Fixed(2) - x;
555 }
556
557 const Fixed x2 = x*x;
558 return sign * x * (Fixed::pi() - x2*(Fixed::two_pi() - 5 - x2*(Fixed::pi() - 3)))/2;
559}
560
561template <typename B, typename I, unsigned int F>
562inline fixed<B, I, F> cos(fixed<B, I, F> x) noexcept
563{
564 return sin(fixed<B, I, F>::half_pi() + x);
565}
566
567template <typename B, typename I, unsigned int F>
568inline fixed<B, I, F> tan(fixed<B, I, F> x) noexcept
569{
570 auto cx = cos(x);
571
572 // Tangent goes to infinity at 90 and -90 degrees.
573 // We can't represent that with fixed-point maths.
574 assert(abs(cx).raw_value() > 1);
575
576 return sin(x) / cx;
577}
578
579namespace detail {
580
581// Calculates atan(x) assuming that x is in the range [0,1]
582template <typename B, typename I, unsigned int F>
583fixed<B, I, F> atan_sanitized(fixed<B, I, F> x) noexcept
584{
585 using Fixed = fixed<B, I, F>;
586 assert(x >= Fixed(0) && x <= Fixed(1));
587
588 constexpr auto fA = Fixed::template from_fixed_point<63>( 716203666280654660ll); // 0.0776509570923569
589 constexpr auto fB = Fixed::template from_fixed_point<63>(-2651115102768076601ll); // -0.287434475393028
590 constexpr auto fC = Fixed::template from_fixed_point<63>( 9178930894564541004ll); // 0.995181681698119 (PI/4 - A - B)
591
592 const auto xx = x * x;
593 return ((fA*xx + fB)*xx + fC)*x;
594}
595
596// Calculate atan(y / x), assuming x != 0.
597//
598// If x is very, very small, y/x can easily overflow the fixed-point range.
599// If q = y/x and q > 1, atan(q) would calculate atan(1/q) as intermediate step
600// anyway. We can shortcut that here and avoid the loss of information, thus
601// improving the accuracy of atan(y/x) for very small x.
602template <typename B, typename I, unsigned int F>
603fixed<B, I, F> atan_div(fixed<B, I, F> y, fixed<B, I, F> x) noexcept
604{
605 using Fixed = fixed<B, I, F>;
606 assert(x != Fixed(0));
607
608 // Make sure y and x are positive.
609 // If y / x is negative (when y or x, but not both, are negative), negate the result to
610 // keep the correct outcome.
611 if (y < Fixed(0)) {
612 if (x < Fixed(0)) {
613 return atan_div(-y, -x);
614 }
615 return -atan_div(-y, x);
616 }
617 if (x < Fixed(0)) {
618 return -atan_div(y, -x);
619 }
620 assert(y >= Fixed(0));
621 assert(x > Fixed(0));
622
623 if (y > x) {
624 return Fixed::half_pi() - detail::atan_sanitized(x / y);
625 }
626 return detail::atan_sanitized(y / x);
627}
628
629}
630
631template <typename B, typename I, unsigned int F>
632fixed<B, I, F> atan(fixed<B, I, F> x) noexcept
633{
634 using Fixed = fixed<B, I, F>;
635 if (x < Fixed(0))
636 {
637 return -atan(-x);
638 }
639
640 if (x > Fixed(1))
641 {
642 return Fixed::half_pi() - detail::atan_sanitized(Fixed(1) / x);
643 }
644
645 return detail::atan_sanitized(x);
646}
647
648template <typename B, typename I, unsigned int F>
649fixed<B, I, F> asin(fixed<B, I, F> x) noexcept
650{
651 using Fixed = fixed<B, I, F>;
652 assert(x >= Fixed(-1) && x <= Fixed(+1));
653
654 const auto yy = Fixed(1) - x * x;
655 if (yy == Fixed(0))
656 {
657 return copysign(Fixed::half_pi(), x);
658 }
659 return detail::atan_div(x, sqrt(yy));
660}
661
662template <typename B, typename I, unsigned int F>
663fixed<B, I, F> acos(fixed<B, I, F> x) noexcept
664{
665 using Fixed = fixed<B, I, F>;
666 assert(x >= Fixed(-1) && x <= Fixed(+1));
667
668 if (x == Fixed(-1))
669 {
670 return Fixed::pi();
671 }
672 const auto yy = Fixed(1) - x * x;
673 return Fixed(2)*detail::atan_div(sqrt(yy), Fixed(1) + x);
674}
675
676template <typename B, typename I, unsigned int F>
677fixed<B, I, F> atan2(fixed<B, I, F> y, fixed<B, I, F> x) noexcept
678{
679 using Fixed = fixed<B, I, F>;
680
681 if (x == Fixed(0) && y == Fixed(0))
682 {
683 return Fixed(0);
684 }
685
686 if (x == Fixed(0)) {
687 return (y > Fixed(0)) ? Fixed::half_pi() : -Fixed::half_pi();
688 }
689
690 auto ret = detail::atan_div(y, x);
691
692 if (x < Fixed(0))
693 {
694 return (y >= Fixed(0)) ? ret + Fixed::pi() : ret - Fixed::pi();
695 }
696 return ret;
697}
698
699}