FixedMath.hpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2017 Louis Langholtz https://github.com/louis-langholtz/PlayRho
3  *
4  * This software is provided 'as-is', without any express or implied
5  * warranty. In no event will the authors be held liable for any damages
6  * arising from the use of this software.
7  *
8  * Permission is granted to anyone to use this software for any purpose,
9  * including commercial applications, and to alter it and redistribute it
10  * freely, subject to the following restrictions:
11  *
12  * 1. The origin of this software must not be misrepresented; you must not
13  * claim that you wrote the original software. If you use this software
14  * in a product, an acknowledgment in the product documentation would be
15  * appreciated but is not required.
16  * 2. Altered source versions must be plainly marked as such, and must not be
17  * misrepresented as being the original software.
18  * 3. This notice may not be removed or altered from any source distribution.
19  */
20 
21 #ifndef PLAYRHO_COMMON_FIXEDMATH_HPP
22 #define PLAYRHO_COMMON_FIXEDMATH_HPP
23 
24 #include <PlayRho/Common/Fixed.hpp>
25 #include <cmath>
26 
27 namespace playrho {
28 
44 
47 template <typename BT, unsigned int FB, int N = 5>
48 constexpr inline Fixed<BT, FB> abs(Fixed<BT, FB> arg)
49 {
50  return arg >= 0? arg: -arg;
51 }
52 
58 template <typename BT, unsigned int FB>
59 constexpr Fixed<BT, FB> pow(Fixed<BT, FB> value, int n)
60 {
61  if (!n)
62  {
63  return Fixed<BT, FB>{1};
64  }
65  if (value == 0)
66  {
67  if (n > 0)
68  {
69  return Fixed<BT, FB>{0};
70  }
72  }
73  if (value == 1)
74  {
75  return Fixed<BT, FB>{1};
76  }
78  {
79  if (n > 0)
80  {
81  if (n % 2 == 0)
82  {
84  }
86  }
87  return Fixed<BT, FB>{0};
88  }
89  if (value == Fixed<BT, FB>::GetInfinity())
90  {
91  return (n < 0)? Fixed<BT, FB>{0}: Fixed<BT, FB>::GetInfinity();
92  }
93 
94  const auto doReciprocal = (n < 0);
95  if (doReciprocal)
96  {
97  n = -n;
98  }
99 
100  auto res = value;
101  for (; n > 1; --n)
102  {
103  res *= value;
104  }
105 
106  return (doReciprocal)? 1 / res: res;
107 }
108 
109 namespace detail {
110 
112 template <typename BT, unsigned int FB>
113 constexpr const auto FixedPi = Fixed<BT, FB>{3.14159265358979323846264338327950288};
114 
116 constexpr inline auto factorial(std::int64_t n)
117 {
118  // n! = n·(n – 1)·(n – 2) · · · 3·2·1
119  auto res = n;
120  for (--n; n > 1; --n)
121  {
122  res *= n;
123  }
124  return res;
125 }
126 
133 template <typename BT, unsigned int FB, int N = 6>
134 constexpr inline Fixed<BT, FB> exp(Fixed<BT, FB> arg)
135 {
136  const auto doReciprocal = (arg < 0);
137  if (doReciprocal)
138  {
139  arg = -arg;
140  }
141 
142  // Maclaurin series approximation...
143  // e^x = sum(x^n/n!) for n =0 to infinity.
144  // e^x = 1 + x + x^2/2! + x^3/3! + ...
145  // Note: e^(x+y) = e^x * e^y.
146  // Note: convergence is slower for arg > 2 and overflow happens by i == 9
147  auto pt = arg;
148  auto res = pt + 1;
149  auto ft = 1;
150  auto last = pt / ft;
151  for (auto i = 2; i < N; ++i)
152  {
153  // have to avoid unnecessarily overflowing...
154  last /= i;
155  last *= arg;
156  res += last;
157  }
158  return doReciprocal? 1 / res: res;
159 }
160 
165 template <typename BT, unsigned int FB, int N = 6>
167 {
168  if (arg.isnan() || (arg < 0))
169  {
170  return Fixed<BT, FB>::GetNaN();
171  }
172  if (arg == 0)
173  {
175  }
176  if (arg == 1)
177  {
178  return Fixed<BT, FB>{0};
179  }
180  if (arg == Fixed<BT, FB>::GetInfinity())
181  {
183  }
184  if (arg <= 2)
185  {
186  // ln(x) = sum((-1)^(n + 1) * (x - 1)^n / n) from n = 1 to infinity
187  // ln(x) = (x - 1) - (x - 1)^2/2 + (x - 1)^3/3 - (x - 1)^4/4 ....
188  arg -= 1;
189  auto res = arg;
190  auto sign = -1;
191  auto pt = arg;
192  for (auto i = 2; i < N; ++i)
193  {
194  pt *= arg;
195  res += sign * pt / i;
196  sign = -sign;
197  }
198  return res;
199  }
200 
201  // The following algorithm isn't as accurate as desired.
202  // Is there a better one?
203  // ln(x) = ((x - 1) / x) + ((x - 1) / x)^2/2 + ((x - 1) / x)^3/3 + ...
204  arg = (arg - 1) / arg;
205  auto pt = arg;
206  auto res = pt;
207  for (auto i = 2; i < N; ++i)
208  {
209  pt *= arg;
210  res += pt / i;
211  }
212  return res;
213 }
214 
217 template <typename BT, unsigned int FB, int N = 5>
218 constexpr inline Fixed<BT, FB> sin(Fixed<BT, FB> arg)
219 {
220  // Maclaurin series approximation...
221  // sin x = sum((-1^n)*(x^(2n+1))/(2n+1)!)
222  // sin(2) = 0.90929742682
223  // x - x^3/6 + x^5/120 - x^7/5040 + x^9/
224  // 2 - 8/6 = 0.666
225  // 2 - 8/6 + 32/120 = 0.9333
226  // 2 - 8/6 + 32/120 - 128/5040 = 0.90793650793
227  // 2 - 8/6 + 32/120 - 128/5040 + 512/362880 = 0.90934744268
228  auto res = arg;
229  auto sgn = -1;
230  constexpr const auto last = 2 * N + 1;
231  auto pt = arg;
232  auto ft = 1;
233  for (auto i = 3; i <= last; i += 2)
234  {
235  ft *= (i - 1) * i;
236  pt *= arg * arg;
237  const auto term = pt / ft;
238  res += sgn * term;
239  sgn = -sgn;
240  }
241  return res;
242 }
243 
246 template <typename BT, unsigned int FB, int N = 5>
247 constexpr inline Fixed<BT, FB> cos(Fixed<BT, FB> arg)
248 {
249  // Maclaurin series approximation...
250  // cos x = sum((-1^n)*(x^(2n))/(2n)!)
251  // cos(2) = -0.41614683654
252  // 1 - 2^2/2 = -1
253  // 1 - 2^2/2 + 2^4/24 = -0.3333
254  // 1 - 2^2/2 + 2^4/24 - 2^6/720 = -0.422
255  auto res = Fixed<BT, FB>{1};
256  auto sgn = -1;
257  constexpr const auto last = 2 * N;
258  auto ft = 1;
259  auto pt = Fixed<BT, FB>{1};
260  for (auto i = 2; i <= last; i += 2)
261  {
262  ft *= (i - 1) * i;
263  pt *= arg * arg;
264  const auto term = pt / ft;
265  res += sgn * term;
266  sgn = -sgn;
267  }
268  return res;
269 }
270 
274 template <typename BT, unsigned int FB, int N = 5>
275 constexpr inline Fixed<BT, FB> atan(Fixed<BT, FB> arg)
276 {
277  // Note: if (x > 0) then arctan(x) == Pi/2 - arctan(1/x)
278  // if (x < 0) then arctan(x) == -Pi/2 - arctan(1/x).
279  const auto doReciprocal = (abs(arg) > 1);
280  if (doReciprocal)
281  {
282  arg = 1 / arg;
283  }
284 
285  // Maclaurin series approximation...
286  // For |arg| <= 1, arg != +/- i
287  // If |arg| > 1 the result is too wrong which is why the reciprocal is done then.
288  auto res = arg;
289  auto sgn = -1;
290  const auto last = 2 * N + 1;
291  auto pt = arg;
292  for (auto i = 3; i <= last; i += 2)
293  {
294  pt *= arg * arg;
295  const auto term = pt / i;
296  res += sgn * term;
297  sgn = -sgn;
298  }
299 
300  if (doReciprocal)
301  {
302  return (arg > 0)? FixedPi<BT, FB> / 2 - res: -FixedPi<BT, FB> / 2 - res;
303  }
304  return res;
305 }
306 
309 template <typename BT, unsigned int FB>
310 constexpr inline auto ComputeSqrt(Fixed<BT, FB> arg)
311 {
312  auto temp = Fixed<BT, FB>{1};
313  auto tempSquared = Square(temp);
314  const auto greaterThanOne = arg > 1;
315  auto lower = greaterThanOne? Fixed<BT, FB>{1}: arg;
316  auto upper = greaterThanOne? arg: Fixed<BT, FB>{1};
317  while (arg != tempSquared)
318  {
319  const auto mid = (lower + upper) / 2;
320  if (temp == mid)
321  {
322  break;
323  }
324  temp = mid;
325  tempSquared = Square(temp);
326  if (tempSquared > arg)
327  {
328  upper = temp;
329  }
330  else if (tempSquared < arg)
331  {
332  lower = temp;
333  }
334  }
335  return temp;
336 }
337 
338 } // namespace detail
339 
342 template <typename BT, unsigned int FB>
343 constexpr inline Fixed<BT, FB> trunc(Fixed<BT, FB> arg)
344 {
345  return static_cast<Fixed<BT, FB>>(static_cast<long long>(arg));
346 }
347 
350 template <typename BT, unsigned int FB>
352 {
353  if (from < to)
354  {
355  return static_cast<Fixed<BT, FB>>(from + Fixed<BT,FB>::GetMin());
356  }
357  if (from > to)
358  {
359  return static_cast<Fixed<BT, FB>>(from - Fixed<BT,FB>::GetMin());
360  }
361  return static_cast<Fixed<BT, FB>>(to);
362 }
363 
366 template <typename BT, unsigned int FB>
367 inline Fixed<BT, FB> fmod(Fixed<BT, FB> dividend, Fixed<BT, FB> divisor) noexcept
368 {
369  const auto quotient = dividend / divisor;
370  const auto integer = trunc(quotient);
371  const auto remainder = quotient - integer;
372  return remainder * divisor;
373 }
374 
383 template <typename BT, unsigned int FB>
384 inline auto sqrt(Fixed<BT, FB> arg)
385 {
386  if ((arg == Fixed<BT, FB>{1}) || (arg == Fixed<BT, FB>{0}))
387  {
388  return arg;
389  }
390  if (arg > Fixed<BT, FB>{0})
391  {
392  return detail::ComputeSqrt(arg);
393  }
394  // else arg < 0 or NaN...
395  return Fixed<BT, FB>::GetNaN();
396 }
397 
400 template <typename BT, unsigned int FB>
401 inline bool isnormal(Fixed<BT, FB> arg)
402 {
403  return arg != Fixed<BT, FB>{0} && arg.isfinite();
404 }
405 
406 namespace detail {
407 
409 template <typename BT, unsigned int FB>
410 inline auto AngularNormalize(Fixed<BT, FB> angleInRadians)
411 {
412  constexpr const auto oneRotationInRadians = 2 * FixedPi<BT, FB>;
413 
414  angleInRadians = fmod(angleInRadians, oneRotationInRadians);
415  if (angleInRadians > FixedPi<BT, FB>)
416  {
417  // 190_deg becomes 190_deg - 360_deg = -170_deg
418  angleInRadians -= oneRotationInRadians;
419  }
420  else if (angleInRadians < -FixedPi<BT, FB>)
421  {
422  // -200_deg becomes -200_deg + 360_deg = 100_deg
423  angleInRadians += oneRotationInRadians;
424  }
425  return angleInRadians;
426 }
427 
428 } // namespace detail
429 
432 template <typename BT, unsigned int FB>
434 {
435  arg = detail::AngularNormalize(arg);
436  return detail::sin<BT, FB, 5>(arg);
437 }
438 
441 template <typename BT, unsigned int FB>
443 {
444  arg = detail::AngularNormalize(arg);
445  return detail::cos<BT, FB, 5>(arg);
446 }
447 
451 template <typename BT, unsigned int FB>
453 {
454  if (arg.isnan() || (arg == 0))
455  {
456  return arg;
457  }
458  if (arg == Fixed<BT, FB>::GetInfinity())
459  {
460  return detail::FixedPi<BT, FB> / 2;
461  }
463  {
464  return -detail::FixedPi<BT, FB> / 2;
465  }
466  return detail::atan<BT, FB, 5>(arg);
467 }
468 
472 template <typename BT, unsigned int FB>
474 {
475  // See https://en.wikipedia.org/wiki/Atan2
476  // See https://en.wikipedia.org/wiki/Taylor_series
477  if (x > 0)
478  {
479  return atan(y / x);
480  }
481  if (x < 0)
482  {
483  return atan(y / x) + ((y >= 0)? +1: -1) * detail::FixedPi<BT, FB>;
484  }
485  if (y > 0)
486  {
487  return +detail::FixedPi<BT, FB> / 2;
488  }
489  if (y < 0)
490  {
491  return -detail::FixedPi<BT, FB> / 2;
492  }
493  return Fixed<BT, FB>::GetNaN();
494 }
495 
498 template <typename BT, unsigned int FB>
500 {
501  return (arg < 8)? detail::log<BT, FB, 36>(arg): detail::log<BT, FB, 96>(arg);
502 }
503 
506 template <typename BT, unsigned int FB>
508 {
509  return (arg <= 2)? detail::exp<BT, FB, 6>(arg): detail::exp<BT, FB, 24>(arg);
510 }
511 
514 template <typename BT, unsigned int FB>
516 {
517  if (exponent.isfinite())
518  {
519  const auto intExp = static_cast<int>(exponent);
520  if (intExp == exponent)
521  {
522  // fall back to integer version...
523  return pow(base, intExp);
524  }
525  }
526 
527  if (base < 0)
528  {
529  return Fixed<BT, FB>::GetNaN();
530  }
531 
532  const auto lnResult = log(base);
533  const auto expResult = exp(lnResult * exponent);
534  return expResult;
535 }
536 
539 template <typename BT, unsigned int FB>
541 {
542  return sqrt(x * x + y * y);
543 }
544 
547 template <typename BT, unsigned int FB>
548 inline Fixed<BT, FB> round(Fixed<BT, FB> value) noexcept
549 {
550  const auto tmp = value + (Fixed<BT, FB>{1} / Fixed<BT, FB>{2});
551  const auto truncated = static_cast<typename Fixed<BT, FB>::value_type>(tmp);
552  return Fixed<BT, FB>{truncated, 0};
553 }
554 
557 template <typename BT, unsigned int FB>
558 inline bool signbit(Fixed<BT, FB> value) noexcept
559 {
560  return value.getsign() < 0;
561 }
562 
565 template <typename BT, unsigned int FB>
566 PLAYRHO_CONSTEXPR inline bool isnan(Fixed<BT, FB> value) noexcept
567 {
568  return value.Compare(0) == Fixed<BT, FB>::CmpResult::Incomparable;
569 }
570 
573 template <typename BT, unsigned int FB>
574 inline bool isfinite(Fixed<BT, FB> value) noexcept
575 {
576  return (value > Fixed<BT, FB>::GetNegativeInfinity())
577  && (value < Fixed<BT, FB>::GetInfinity());
578 }
579 
581 
582 } // namespace playrho
583 
584 #endif // PLAYRHO_COMMON_FIXEDMATH_HPP