#pragma once #include namespace rack { namespace dsp { /* Glossary: https://en.wikipedia.org/wiki/Taylor_series https://en.wikipedia.org/wiki/Chebyshev_polynomials https://en.wikipedia.org/wiki/Pad%C3%A9_approximant https://en.wikipedia.org/wiki/Horner%27s_method https://en.wikipedia.org/wiki/Estrin%27s_scheme https://en.wikipedia.org/wiki/CORDIC */ /** Evaluates a polynomial with coefficients `a[n]` at `x`. Uses naive direct evaluation. */ template T polyDirect(const T (&a)[N], T x) { T y = 0; T xn = 1; for (size_t n = 0; n < N; n++) { y += a[n] * xn; xn *= x; } return y; } /** Evaluates a polynomial with coefficients `a[n]` at `x`. Uses Horner's method. https://en.wikipedia.org/wiki/Horner%27s_method */ template T polyHorner(const T (&a)[N], T x) { if (N == 0) return 0; T y = a[N - 1]; for (size_t n = 1; n < N; n++) { y = a[N - 1 - n] + y * x; } return y; } /** Evaluates a polynomial with coefficients `a[n]` at `x`. Uses Estrin's method. https://en.wikipedia.org/wiki/Estrin%27s_scheme */ template T polyEstrin(const T (&a)[N], T x) { if (N == 0) return 0; if (N == 1) return a[0]; const size_t M = (N + 1) / 2; T b[M]; for (size_t i = 0; i < M; i++) { b[i] = a[2 * i]; if (2 * i + 1 < N) b[i] += a[2 * i + 1] * x; } return polyEstrin(b, x * x); } /** Returns `2^floor(x)`. If xf is given, sets it to the fractional part of x. This is useful in the computation `2^x = 2^floor(x) * 2^frac(x)`. */ template T exp2Floor(T x, T* xf); template <> inline float exp2Floor(float x, float* xf) { x += 127; // x should be positive now, so this always truncates towards -inf. int32_t xi = x; if (xf) *xf = x - xi; // Set mantissa of float union { float yi; int32_t yii; }; yii = xi << 23; return yi; } template <> inline simd::float_4 exp2Floor(simd::float_4 x, simd::float_4* xf) { x += 127; simd::int32_4 xi = x; if (xf) *xf = x - simd::float_4(xi); simd::int32_4 yii = xi << 23; return simd::float_4::cast(yii); } /** Deprecated alias of exp2Floor() */ template T approxExp2Floor(T x, T* xf) { return exp2Floor(x, xf); } /** Returns 2^x with at most 6e-06 relative error. Polynomial coefficients are chosen to minimize relative error while maintaining continuity and giving exact values at integer values of `x`. Thanks to Andy Simper for coefficients. */ template T exp2_taylor5(T x) { T xf; T yi = exp2Floor(x, &xf); const T a[] = { 1.0, 0.69315169353961, 0.2401595990753, 0.055817908652, 0.008991698010, 0.001879100722, }; T yf = polyHorner(a, xf); return yi * yf; } /** Deprecated alias of exp2_taylor5() */ template T approxExp2_taylor5(T x) { return exp2_taylor5(x); } } // namespace dsp } // namespace rack