You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

143 lines
2.8KB

  1. #pragma once
  2. #include <dsp/common.hpp>
  3. namespace rack {
  4. namespace dsp {
  5. /*
  6. Glossary:
  7. https://en.wikipedia.org/wiki/Taylor_series
  8. https://en.wikipedia.org/wiki/Chebyshev_polynomials
  9. https://en.wikipedia.org/wiki/Pad%C3%A9_approximant
  10. https://en.wikipedia.org/wiki/Horner%27s_method
  11. https://en.wikipedia.org/wiki/Estrin%27s_scheme
  12. https://en.wikipedia.org/wiki/CORDIC
  13. */
  14. /** Evaluates a polynomial with coefficients `a[n]` at `x`.
  15. Uses naive direct evaluation.
  16. */
  17. template <typename T, size_t N>
  18. T polyDirect(const T (&a)[N], T x) {
  19. T y = 0;
  20. T xn = 1;
  21. for (size_t n = 0; n < N; n++) {
  22. y += a[n] * xn;
  23. xn *= x;
  24. }
  25. return y;
  26. }
  27. /** Evaluates a polynomial with coefficients `a[n]` at `x`.
  28. Uses Horner's method.
  29. https://en.wikipedia.org/wiki/Horner%27s_method
  30. */
  31. template <typename T, size_t N>
  32. T polyHorner(const T (&a)[N], T x) {
  33. if (N == 0)
  34. return 0;
  35. T y = a[N - 1];
  36. for (size_t n = 1; n < N; n++) {
  37. y = a[N - 1 - n] + y * x;
  38. }
  39. return y;
  40. }
  41. /** Evaluates a polynomial with coefficients `a[n]` at `x`.
  42. Uses Estrin's method.
  43. https://en.wikipedia.org/wiki/Estrin%27s_scheme
  44. */
  45. template <typename T, size_t N>
  46. T polyEstrin(const T (&a)[N], T x) {
  47. if (N == 0)
  48. return 0;
  49. if (N == 1)
  50. return a[0];
  51. const size_t M = (N + 1) / 2;
  52. T b[M];
  53. for (size_t i = 0; i < M; i++) {
  54. b[i] = a[2 * i];
  55. if (2 * i + 1 < N)
  56. b[i] += a[2 * i + 1] * x;
  57. }
  58. return polyEstrin(b, x * x);
  59. }
  60. /** Returns `2^floor(x)`.
  61. If xf is given, sets it to the fractional part of x.
  62. This is useful in the computation `2^x = 2^floor(x) * 2^frac(x)`.
  63. */
  64. template <typename T>
  65. T exp2Floor(T x, T* xf);
  66. template <>
  67. inline float exp2Floor(float x, float* xf) {
  68. x += 127;
  69. // x should be positive now, so this always truncates towards -inf.
  70. int32_t xi = x;
  71. if (xf)
  72. *xf = x - xi;
  73. // Set mantissa of float
  74. union {
  75. float yi;
  76. int32_t yii;
  77. };
  78. yii = xi << 23;
  79. return yi;
  80. }
  81. template <>
  82. inline simd::float_4 exp2Floor(simd::float_4 x, simd::float_4* xf) {
  83. x += 127;
  84. simd::int32_4 xi = x;
  85. if (xf)
  86. *xf = x - simd::float_4(xi);
  87. simd::int32_4 yii = xi << 23;
  88. return simd::float_4::cast(yii);
  89. }
  90. /** Deprecated alias of exp2Floor() */
  91. template <typename T>
  92. T approxExp2Floor(T x, T* xf) {
  93. return exp2Floor(x, xf);
  94. }
  95. /** Returns 2^x with at most 6e-06 relative error.
  96. Polynomial coefficients are chosen to minimize relative error while maintaining continuity and giving exact values at integer values of `x`.
  97. Thanks to Andy Simper for coefficients.
  98. */
  99. template <typename T>
  100. T exp2_taylor5(T x) {
  101. T xf;
  102. T yi = exp2Floor(x, &xf);
  103. const T a[] = {
  104. 1.0,
  105. 0.69315169353961,
  106. 0.2401595990753,
  107. 0.055817908652,
  108. 0.008991698010,
  109. 0.001879100722,
  110. };
  111. T yf = polyHorner(a, xf);
  112. return yi * yf;
  113. }
  114. /** Deprecated alias of exp2_taylor5() */
  115. template <typename T>
  116. T approxExp2_taylor5(T x) {
  117. return exp2_taylor5(x);
  118. }
  119. } // namespace dsp
  120. } // namespace rack