FastLED 3.10.8
Loading...
Searching...
No Matches
math.cpp.hpp
Go to the documentation of this file.
1#include "fl/math/math.h"
2#include "fl/system/sketch_macros.h" // SKETCH_HAS_LARGE_MEMORY
3
4// FL_MATH_USE_LIBM gate: on Large-memory targets we use libm for full IEEE-
5// 754 precision. On Low-memory targets (where SKETCH_HAS_LARGE_MEMORY=0:
6// AVR, LPC8xx, Teensy 3.x/LC, STM32F1, ESP8266, ...) libm transitively
7// anchors libgcc's soft-double helper set (`__aeabi_dadd`, `__aeabi_dmul`,
8// `__aeabi_ddiv`, ...). On the LPC845-BRK JSON-RPC bring-up sketch (#3002)
9// that was ~6 KB of overhead — more than 10 % of the entire 64 KB flash
10// budget — for math operations that the sketch never actually performed.
11//
12// Sketches on Low-memory targets that need full IEEE-754 precision can
13// `#include <cmath>` directly; that anchors libm only at the call site
14// that opted in.
15#ifndef FL_MATH_USE_LIBM
16# if SKETCH_HAS_LARGE_MEMORY
17# define FL_MATH_USE_LIBM 1
18# else
19# define FL_MATH_USE_LIBM 0
20# endif
21#endif
22
23#if FL_MATH_USE_LIBM
24// IWYU pragma: begin_keep
25#include <math.h>
26#include "fl/stl/noexcept.h"
27// IWYU pragma: end_keep // okay banned header (STL wrapper implementation requires standard math.h)
28#endif
29
30// Arduino's WString.h (transitively included via the Arduino core on AVR and
31// other Arduino-framework targets) defines `F()` as a PROGMEM-string macro.
32// That macro collides with the `template <typename F>` parameter used by
33// the polynomial / Newton-iteration helpers in this file: every `F(value)`
34// constructor cast (e.g. `F(0.5)`) is rewritten to `WString::F(0.5)` and
35// fails to compile. Undefining the macro here is local to this translation
36// unit; the macro is only used in user sketches, never in fl::math itself.
37#ifdef F
38#undef F
39#endif
40
41namespace fl {
42
43// Standalone floor implementation for float
45 if (value >= 0.0f) {
46 return static_cast<float>(static_cast<int>(value));
47 }
48 int i = static_cast<int>(value);
49 return static_cast<float>(i - (value != static_cast<float>(i) ? 1 : 0));
50}
51
52// Standalone floor implementation for double
54 if (value >= 0.0) {
55 return static_cast<double>(static_cast<long long>(value));
56 }
57 long long i = static_cast<long long>(value);
58 return static_cast<double>(i - (value != static_cast<double>(i) ? 1 : 0));
59}
60
61// Standalone ceil implementation for float
63 if (value <= 0.0f) {
64 return static_cast<float>(static_cast<int>(value));
65 }
66 int i = static_cast<int>(value);
67 return static_cast<float>(i + (value != static_cast<float>(i) ? 1 : 0));
68}
69
70// Standalone ceil implementation for double
72 if (value <= 0.0) {
73 return static_cast<double>(static_cast<long long>(value));
74 }
75 long long i = static_cast<long long>(value);
76 return static_cast<double>(i + (value != static_cast<double>(i) ? 1 : 0));
77}
78
79// exp: range reduction + short polynomial, libm-grade on every target, and
80// the only exp implementation (no libm path).
81//
82// e^x = 2^k * e^r with k = round(x / ln2) and |r| <= ln2/2 = 0.3466. On that
83// interval a degree-6 minimax polynomial (float) / fdlibm's rational form
84// (double) is accurate to the last bit, and 2^k is an exponent-field write with no
85// rounding. The previous implementation evaluated a ten-term series at the
86// raw x and clamped at +-10: exact for |x| <= 1, 3 % off at 5, 54 % off at 10,
87// and wrong by orders of magnitude beyond (#4288).
88//
89// Cost matters on cores without an FPU, where every float operation and
90// every conversion is a library call, so the surrounding work is integer:
91// range checks read the exponent bits, k comes from the "magic number"
92// rounding trick instead of float->int->float conversions, and type punning
93// goes through a union rather than fl::memcpy (an out-of-line byte loop).
94//
95// Reduction uses the Cody-Waite split of ln2 (ln2_hi has trailing zero
96// bits so k * ln2_hi is exact for every k in range). Constants are the
97// fdlibm/musl values.
98namespace detail {
99
100union FloatBits { float f; u32 u; i32 i; };
101union DoubleBits { double f; u64 u; i64 i; };
102
103// p * 2^k built from the exponent field. k is split in two so each half is
104// a normal float, and p is scaled by the halves in turn: the intermediate
105// stays finite for k = 128 (p < 1) and the only rounding, for subnormal
106// results, happens once in the final multiply.
107inline float scale_pow2_(float p, int k) FL_NO_EXCEPT {
108 const int a = k / 2;
109 const int b = k - a;
110 FloatBits fa, fb;
111 fa.u = static_cast<u32>(a + 127) << 23;
112 fb.u = static_cast<u32>(b + 127) << 23;
113 return (p * fa.f) * fb.f;
114}
115
116inline double scale_pow2_(double p, int k) FL_NO_EXCEPT {
117 const int a = k / 2;
118 const int b = k - a;
119 DoubleBits fa, fb;
120 fa.u = static_cast<u64>(a + 1023) << 52;
121 fb.u = static_cast<u64>(b + 1023) << 52;
122 return (p * fa.f) * fb.f;
123}
124
125inline float exp_reduced_(float x) FL_NO_EXCEPT {
126 FloatBits xb;
127 xb.f = x;
128 const u32 ax = xb.u & 0x7fffffffu;
129 if (ax >= 0x42b17218u) { // |x| >= 88.7228, or NaN
130 if (ax > 0x7f800000u) return x; // NaN
131 if ((xb.u >> 31) == 0u) { // overflow -> +inf
132 FloatBits inf;
133 inf.u = 0x7f800000u;
134 return inf.f;
135 }
136 if (ax >= 0x42cff1b5u) return 0.0f; // x <= -103.972: underflow
137 }
138 // k = round(x / ln2) via the 1.5 * 2^23 magic constant: adding it puts
139 // the integer in the low mantissa bits, so no float<->int conversions.
140 const float kMagic = 12582912.0f;
141 FloatBits t;
142 t.f = x * 1.44269504088896341f + kMagic;
143 const int k = t.i - 0x4b400000;
144 const float kf = t.f - kMagic;
145 const float r = (x - kf * 6.9314575195e-01f) - kf * 1.4286067653e-06f;
146 // e^r = 1 + r + r^2 * P(r), P minimax on |r| <= ln2/2: worst case 1 ulp.
147 float p = 1.393364297e-03f;
148 p = p * r + 8.363175565e-03f;
149 p = p * r + 4.166646498e-02f;
150 p = p * r + 1.666657700e-01f;
151 p = p * r + 5.000000013e-01f;
152 p = (p * r * r) + r + 1.0f;
153 return scale_pow2_(p, k);
154}
155
156inline double exp_reduced_(double x) FL_NO_EXCEPT {
157 // AVR builds double as 32 bits: the float path is the double path there,
158 // and the 64-bit bit patterns below would read past the value.
159 if (sizeof(double) == sizeof(float)) {
160 return static_cast<double>(exp_reduced_(static_cast<float>(x)));
161 }
162 DoubleBits xb;
163 xb.f = x;
164 const u64 ax = xb.u & 0x7fffffffffffffffull;
165 if (ax >= 0x40862e42fefa39efull) { // |x| >= 709.7827, or NaN
166 if (ax > 0x7ff0000000000000ull) return x; // NaN
167 if ((xb.u >> 63) == 0u) { // overflow -> +inf
168 DoubleBits inf;
169 inf.u = 0x7ff0000000000000ull;
170 return inf.f;
171 }
172 if (ax >= 0x40874910d52d3052ull) return 0.0; // x <= -745.133: underflow
173 }
174 const double kMagic = 6755399441055744.0; // 1.5 * 2^52
176 t.f = x * 1.44269504088896338700e+00 + kMagic;
177 const int k = static_cast<int>(t.i - 0x4338000000000000ll);
178 const double kd = t.f - kMagic;
179 // fdlibm e_exp: e^r = 1 + 2r/(2 - c(r)) with c a degree-5 polynomial in
180 // r^2. Five multiply-adds and one division reach < 1 ulp, half the work of
181 // a Taylor series of the same accuracy; that matters on cores that do
182 // double in software.
183 const double hi = x - kd * 6.93147180369123816490e-01;
184 const double lo = kd * 1.90821492927058770002e-10;
185 const double r = hi - lo;
186 const double r2 = r * r;
187 const double c = r - r2 * (1.66666666666666019037e-01 +
188 r2 * (-2.77777777770155933842e-03 +
189 r2 * (6.61375632143793436117e-05 +
190 r2 * (-1.65339022054652515390e-06 +
191 r2 * 4.13813679705723846039e-08))));
192 const double p = 1.0 - ((lo - (r * c) / (2.0 - c)) - hi);
193 return scale_pow2_(p, k);
194}
195
196} // namespace detail
197
198// exp takes the range-reduced form on every target, including ones with
199// libm: it is within 2 ulp of libm, cheaper than a libm call, and gives the
200// same answers everywhere, so audio goldens do not depend on the host.
203
204// =============================================================================
205// Libm-free trig / sqrt / log / pow implementations
206// =============================================================================
207//
208// Hand-rolled to keep fl::math from anchoring libm in the link on freestanding
209// targets. Previously the wrappers below all called `::sqrtf` / `::sin` /
210// `::log` / etc, which pulled libm into every sketch that touched fl::math —
211// transitively dragging in libgcc's full soft-double helper set (`__aeabi_dadd`,
212// `__aeabi_dmul`, `__aeabi_ddiv`, ...) on no-FPU MCUs. On the LPC845-BRK
213// JSON-RPC bring-up sketch (#3002), that was ~6 KB of overhead — more than
214// 10 % of the entire 64 KB flash budget — for math operations that the sketch
215// never actually performed.
216//
217// Strategy: polynomial / Newton iteration approximations with range reduction.
218// Accuracy target is FastLED's animation use cases (color wheel angles,
219// particle orientation, easing curves, gamma LUT generation) — typically
220// ~3-4 decimal digits relative is plenty. Sketches that need full IEEE-754
221// precision can call `<cmath>` directly; doing so anchors libm at THAT call
222// site only, which is the explicit cost the user opted into.
223
224namespace detail {
225
226// --- sqrt via Newton-Raphson iteration ------------------------------------
227//
228// Initial estimate from the IEEE-754 exponent halving trick (well-known
229// "bit-hack" approach, refined). Two Newton iterations converge to roughly
230// 5 decimal digits for float, 10 for double — more than enough for color
231// space and rotational math.
232template <typename F>
234 if (value <= F(0)) return F(0);
235 // Initial estimate: x_0 = value / 2. Pure-arithmetic, no bit-cast needed.
236 // Newton converges quadratically so 5-6 iterations is plenty even from a
237 // crude start.
238 F x = value;
239 if (x > F(1)) x = F(1) + (x - F(1)) * F(0.5); // bias toward 1 for fast convergence
240 for (int i = 0; i < 6; ++i) {
241 if (x == F(0)) break;
242 x = F(0.5) * (x + value / x);
243 }
244 return x;
245}
246
247// --- Polynomial sin/cos with range reduction ------------------------------
248//
249// Reduce input to [-π/4, π/4] then evaluate degree-7 Taylor polynomial.
250// Max error ~1e-5 for float, ~1e-10 for double on the reduced range.
251template <typename F>
253 // sin(x) ≈ x - x³/6 + x⁵/120 - x⁷/5040
254 const F x2 = x * x;
255 return x * (F(1) - x2 * (F(1.0 / 6.0) - x2 * (F(1.0 / 120.0) - x2 * F(1.0 / 5040.0))));
256}
257
258template <typename F>
260 // cos(x) ≈ 1 - x²/2 + x⁴/24 - x⁶/720
261 const F x2 = x * x;
262 return F(1) - x2 * (F(0.5) - x2 * (F(1.0 / 24.0) - x2 * F(1.0 / 720.0)));
263}
264
265template <typename F>
267 const F kPi = F(3.14159265358979323846);
268 const F kTwoPi = F(6.28318530717958647692);
269 const F kPiHalf = F(1.57079632679489661923);
270 // Reduce to [-π, π]: subtract floor(x / 2π) * 2π.
271 // For the FastLED use cases we expect |x| < ~100, so this loop runs a
272 // bounded number of times. For pathological inputs we cap iterations.
273 int guard = 32;
274 while (x > kPi && guard > 0) { x -= kTwoPi; --guard; }
275 while (x < -kPi && guard > 0) { x += kTwoPi; --guard; }
276 // Map to [-π/2, π/2] using sin(π - x) = sin(x).
277 if (x > kPiHalf) x = kPi - x;
278 if (x < -kPiHalf) x = -kPi - x;
279 // [-π/2, π/2] -- the polynomial works fine here (max |x| ≈ 1.57).
280 return sin_poly_quarterturn_(x);
281}
282
283template <typename F>
285 // cos(x) = sin(π/2 - x)
286 const F kPiHalf = F(1.57079632679489661923);
287 return sin_reduce_(kPiHalf - x);
288}
289
290// --- log via fraction extraction + polynomial -----------------------------
291//
292// log(x) = log(m * 2^e) = log(m) + e * log(2) where m ∈ [1, 2)
293// Repeatedly multiply/divide by 2 to bring x into [1, 2), then evaluate
294// log(m) via a polynomial in (m-1) on [0, 1].
295template <typename F>
297 if (value <= F(0)) return F(-1e30); // -inf surrogate
298 int e = 0;
299 while (value >= F(2)) { value *= F(0.5); ++e; }
300 while (value < F(1)) { value *= F(2); --e; }
301 // value ∈ [1, 2); evaluate log(value) via Taylor around 1: let u = value - 1.
302 // log(1+u) = u - u²/2 + u³/3 - u⁴/4 + ...
303 // Use enough terms for ~5-decimal accuracy on [0, 1].
304 const F u = value - F(1);
305 const F u2 = u * u;
306 const F u3 = u2 * u;
307 const F u4 = u2 * u2;
308 const F u5 = u4 * u;
309 const F u6 = u4 * u2;
310 const F u7 = u6 * u;
311 F log_m = u - u2 * F(0.5) + u3 * F(1.0 / 3.0) - u4 * F(0.25)
312 + u5 * F(0.2) - u6 * F(1.0 / 6.0) + u7 * F(1.0 / 7.0);
313 const F kLn2 = F(0.69314718055994530942);
314 return log_m + F(e) * kLn2;
315}
316
317} // namespace detail
318
319// Each impl below picks libm (Large-memory targets) or polynomial / Newton
320// (Low-memory targets — keeps libm out of the link).
321
323#if FL_MATH_USE_LIBM
324 return ::sqrtf(value);
325#else
327#endif
328}
329
331#if FL_MATH_USE_LIBM
332 return ::sqrt(value);
333#else
335#endif
336}
337
339#if FL_MATH_USE_LIBM
340 return ::sinf(value);
341#else
343#endif
344}
345
347#if FL_MATH_USE_LIBM
348 return ::sin(value);
349#else
351#endif
352}
353
355#if FL_MATH_USE_LIBM
356 return ::cosf(value);
357#else
359#endif
360}
361
363#if FL_MATH_USE_LIBM
364 return ::cos(value);
365#else
367#endif
368}
369
371#if FL_MATH_USE_LIBM
372 return ::logf(value);
373#else
375#endif
376}
377
379#if FL_MATH_USE_LIBM
380 return ::log(value);
381#else
383#endif
384}
385
387#if FL_MATH_USE_LIBM
388 return ::log10f(value);
389#else
390 const float kInvLn10 = 0.43429448190325182765f;
391 return detail::log_natural_<float>(value) * kInvLn10;
392#endif
393}
394
396#if FL_MATH_USE_LIBM
397 return ::log10(value);
398#else
399 const double kInvLn10 = 0.43429448190325182765;
400 return detail::log_natural_<double>(value) * kInvLn10;
401#endif
402}
403
404float pow_impl_float(float base, float exponent) FL_NO_EXCEPT {
405#if FL_MATH_USE_LIBM
406 return ::powf(base, exponent);
407#else
408 if (exponent == 0.0f) return 1.0f;
409 int ie = static_cast<int>(exponent);
410 if (static_cast<float>(ie) == exponent && ie >= -8 && ie <= 8) {
411 float r = 1.0f;
412 if (ie > 0) { for (int i = 0; i < ie; ++i) r *= base; return r; }
413 if (ie < 0) { for (int i = 0; i < -ie; ++i) r *= base; return 1.0f / r; }
414 }
415 if (base <= 0.0f) return 0.0f;
416 return exp_impl_float(exponent * detail::log_natural_<float>(base));
417#endif
418}
419
420double pow_impl_double(double base, double exponent) FL_NO_EXCEPT {
421#if FL_MATH_USE_LIBM
422 return ::pow(base, exponent);
423#else
424 if (exponent == 0.0) return 1.0;
425 int ie = static_cast<int>(exponent);
426 if (static_cast<double>(ie) == exponent && ie >= -8 && ie <= 8) {
427 double r = 1.0;
428 if (ie > 0) { for (int i = 0; i < ie; ++i) r *= base; return r; }
429 if (ie < 0) { for (int i = 0; i < -ie; ++i) r *= base; return 1.0 / r; }
430 }
431 if (base <= 0.0) return 0.0;
432 return exp_impl_double(exponent * detail::log_natural_<double>(base));
433#endif
434}
435
436// Absolute value: pure arithmetic; libm path unnecessary, same on both.
438 return value < 0.0f ? -value : value;
439}
440
442 return value < 0.0 ? -value : value;
443}
444
446#if FL_MATH_USE_LIBM
447 return ::lroundf(value);
448#else
449 if (value >= 0.0f) return static_cast<long>(value + 0.5f);
450 return static_cast<long>(value - 0.5f);
451#endif
452}
453
455#if FL_MATH_USE_LIBM
456 return ::lround(value);
457#else
458 if (value >= 0.0) return static_cast<long>(value + 0.5);
459 return static_cast<long>(value - 0.5);
460#endif
461}
462
463// Arduino.h defines `round` as a macro, so we temporarily hide it.
464#pragma push_macro("round")
465#undef round
467#if FL_MATH_USE_LIBM
468 return ::roundf(value);
469#else
470 return static_cast<float>(lround_impl_float(value));
471#endif
472}
473
475#if FL_MATH_USE_LIBM
476 return ::round(value);
477#else
478 return static_cast<double>(lround_impl_double(value));
479#endif
480}
481#pragma pop_macro("round")
482
483float fmod_impl_float(float x, float y) FL_NO_EXCEPT {
484#if FL_MATH_USE_LIBM
485 return ::fmodf(x, y);
486#else
487 if (y == 0.0f) return 0.0f;
488 const float q = x / y;
489 // Truncate toward zero (libm fmod convention).
490 const float t = q >= 0.0f ? floor_impl_float(q) : ceil_impl_float(q);
491 return x - t * y;
492#endif
493}
494
495double fmod_impl_double(double x, double y) FL_NO_EXCEPT {
496#if FL_MATH_USE_LIBM
497 return ::fmod(x, y);
498#else
499 if (y == 0.0) return 0.0;
500 const double q = x / y;
501 const double t = q >= 0.0 ? floor_impl_double(q) : ceil_impl_double(q);
502 return x - t * y;
503#endif
504}
505
506// Inverse tangent 2 implementations (atan2).
507//
508// Hand-rolled to avoid pulling libm into the link on freestanding targets
509// (Cortex-M0+, AVR, etc). On the LPC845-BRK bring-up firmware (#3002), this
510// wrapper used to anchor `::atan2`, which transitively pulled in libm's
511// `s_scalbn` / `w_fmod` / `w_acos` and from there the full libgcc soft-double
512// helper set (`__aeabi_dadd`, `__aeabi_dmul`, `__aeabi_ddiv`, ...) — together
513// ~6 KB of pure overhead on a 64 KB-flash part for a sketch that never
514// actually computes an arctangent.
515//
516// Implementation: piecewise minimax polynomial on |y/x| in [0, 1] (degree-5),
517// extended to full [-π, π] via the standard quadrant + reciprocal identities:
518//
519// atan2(y, x) = atan(y/x) for x > 0
520// = atan(y/x) + π for x < 0, y >= 0
521// = atan(y/x) - π for x < 0, y < 0
522// = ±π/2 for x == 0 (sign of y)
523//
524// atan(u) = polynomial(u) for |u| <= 1
525// = π/2 - atan(1/u) for u > 1
526// = -π/2 - atan(1/u) for u < -1
527//
528// The degree-5 polynomial coefficients are Chebyshev-fit on [-1, 1]; max
529// error ≈ 1.5e-3 rad (~0.085°). Adequate for FastLED's animation and
530// rotational-math use cases — color wheel angles, particle orientation,
531// xypath rotations. Sketches that need full double precision should bypass
532// fl::math and call libm directly via `<cmath>` so they get the libm pull
533// only when they actually need it.
534namespace detail {
535template <typename F>
537 // Polynomial approximation of atan on [-1, 1].
538 // Coefficients from Padé-style minimax fit; max abs error ~1.5e-3.
539 const F u2 = u * u;
540 return u * (F(0.99997726) +
541 u2 * (F(-0.33262347) +
542 u2 * (F(0.19354346) +
543 u2 * (F(-0.11643287) +
544 u2 * (F(0.05265332) +
545 u2 * F(-0.01172120))))));
546}
547
548template <typename F>
550 const F kPiHalf = F(1.57079632679489661923);
551 // Reduce to |u| <= 1 via the reciprocal identity.
552 if (u > F(1)) {
553 return kPiHalf - atan_poly_unit_(F(1) / u);
554 } else if (u < F(-1)) {
555 return -kPiHalf - atan_poly_unit_(F(1) / u);
556 }
557 return atan_poly_unit_(u);
558}
559
560template <typename F>
561inline F atan2_full_(F y, F x) FL_NO_EXCEPT {
562 const F kPi = F(3.14159265358979323846);
563 const F kPiHalf = F(1.57079632679489661923);
564 // x == 0 special case: angle is ±π/2 (sign of y), or 0 when both are 0.
565 if (x == F(0)) {
566 if (y > F(0)) return kPiHalf;
567 if (y < F(0)) return -kPiHalf;
568 return F(0);
569 }
570 F a = atan_full_(y / x);
571 if (x < F(0)) {
572 a += (y >= F(0)) ? kPi : -kPi;
573 }
574 return a;
575}
576} // namespace detail
577
578float atan2_impl_float(float y, float x) FL_NO_EXCEPT {
579#if FL_MATH_USE_LIBM
580 return ::atan2f(y, x);
581#else
583#endif
584}
585
586double atan2_impl_double(double y, double x) FL_NO_EXCEPT {
587#if FL_MATH_USE_LIBM
588 return ::atan2(y, x);
589#else
591#endif
592}
593
594float hypot_impl_float(float x, float y) FL_NO_EXCEPT {
595#if FL_MATH_USE_LIBM
596 return ::hypotf(x, y);
597#else
598 return detail::sqrt_newton_<float>(x * x + y * y);
599#endif
600}
601
602double hypot_impl_double(double x, double y) FL_NO_EXCEPT {
603#if FL_MATH_USE_LIBM
604 return ::hypot(x, y);
605#else
606 return detail::sqrt_newton_<double>(x * x + y * y);
607#endif
608}
609
611#if FL_MATH_USE_LIBM
612 return ::atanf(value);
613#else
615#endif
616}
617
619#if FL_MATH_USE_LIBM
620 return ::atan(value);
621#else
623#endif
624}
625
627#if FL_MATH_USE_LIBM
628 return ::asinf(value);
629#else
630 const float kPiHalf = 1.57079632679489661923f;
631 if (value >= 1.0f) return kPiHalf;
632 if (value <= -1.0f) return -kPiHalf;
633 const float denom = detail::sqrt_newton_<float>(1.0f - value * value);
634 if (denom == 0.0f) return value > 0.0f ? kPiHalf : -kPiHalf;
635 return detail::atan_full_<float>(value / denom);
636#endif
637}
638
640#if FL_MATH_USE_LIBM
641 return ::asin(value);
642#else
643 const double kPiHalf = 1.57079632679489661923;
644 if (value >= 1.0) return kPiHalf;
645 if (value <= -1.0) return -kPiHalf;
646 const double denom = detail::sqrt_newton_<double>(1.0 - value * value);
647 if (denom == 0.0) return value > 0.0 ? kPiHalf : -kPiHalf;
648 return detail::atan_full_<double>(value / denom);
649#endif
650}
651
653#if FL_MATH_USE_LIBM
654 return ::acosf(value);
655#else
656 const float kPiHalf = 1.57079632679489661923f;
657 return kPiHalf - asin_impl_float(value);
658#endif
659}
660
662#if FL_MATH_USE_LIBM
663 return ::acos(value);
664#else
665 const double kPiHalf = 1.57079632679489661923;
666 return kPiHalf - asin_impl_double(value);
667#endif
668}
669
671#if FL_MATH_USE_LIBM
672 return ::tanf(value);
673#else
674 const float c = detail::cos_reduce_<float>(value);
675 if (c == 0.0f) return 0.0f;
677#endif
678}
679
681#if FL_MATH_USE_LIBM
682 return ::tan(value);
683#else
684 const double c = detail::cos_reduce_<double>(value);
685 if (c == 0.0) return 0.0;
687#endif
688}
689
690// Load exponent implementations (ldexp): value * 2^exp.
691//
692// Hand-rolled (no libm) for the same reason as atan2 above — `::ldexp` /
693// `::ldexpf` was the other observed external libm entry from `fl.math+` in
694// the LPC845-BRK bring-up firmware map.
695//
696// Strategy: scale by repeated doubling/halving rather than touching IEEE-754
697// exponent bits directly. This is portable (no bit_cast assumptions, no
698// endianness dependence, no signaling-NaN risk) and the worst-case loop
699// count is bounded by the float/double exponent range (~127 / ~1023). For
700// the FastLED use cases that anchor this symbol — stb_vorbis decoder,
701// fixed_point's to_float(), animartrix rotational math — exp is typically
702// a small integer (|exp| < 30), so the bounded loop is cheap (≤ 30 mults).
703namespace detail {
704template <typename F>
706 if (value == F(0)) return value;
707 if (exp == 0) return value;
708 if (exp > 0) {
709 // Cap to avoid pathological loops; saturate to ±inf-ish via overflow
710 // (these magnitudes are well outside the IEEE 754 finite range, and
711 // returning a saturated value matches libm's behavior of producing
712 // ±HUGE_VAL on overflow).
713 if (exp > 1024) exp = 1024;
714 while (exp >= 30) { value *= F(1073741824); exp -= 30; } // 2^30
715 while (exp > 0) { value *= F(2); --exp; }
716 return value;
717 }
718 // exp < 0
719 int neg = -exp;
720 if (neg > 1024) neg = 1024;
721 while (neg >= 30) { value *= F(1.0 / 1073741824.0); neg -= 30; }
722 while (neg > 0) { value *= F(0.5); --neg; }
723 return value;
724}
725} // namespace detail
726
728#if FL_MATH_USE_LIBM
729 return ::ldexpf(value, exp);
730#else
732#endif
733}
734
736#if FL_MATH_USE_LIBM
737 return ::ldexp(value, exp);
738#else
740#endif
741}
742
743} // namespace fl
F atan_poly_unit_(F u) FL_NO_EXCEPT
Definition math.cpp.hpp:536
F sin_reduce_(F x) FL_NO_EXCEPT
Definition math.cpp.hpp:266
F cos_poly_quarterturn_(F x) FL_NO_EXCEPT
Definition math.cpp.hpp:259
float scale_pow2_(float p, int k) FL_NO_EXCEPT
Definition math.cpp.hpp:107
F atan2_full_(F y, F x) FL_NO_EXCEPT
Definition math.cpp.hpp:561
F sin_poly_quarterturn_(F x) FL_NO_EXCEPT
Definition math.cpp.hpp:252
F cos_reduce_(F x) FL_NO_EXCEPT
Definition math.cpp.hpp:284
F log_natural_(F value) FL_NO_EXCEPT
Definition math.cpp.hpp:296
F sqrt_newton_(F value) FL_NO_EXCEPT
Definition math.cpp.hpp:233
float exp_reduced_(float x) FL_NO_EXCEPT
Definition math.cpp.hpp:125
F atan_full_(F u) FL_NO_EXCEPT
Definition math.cpp.hpp:549
F ldexp_loop_(F value, int exp) FL_NO_EXCEPT
Definition math.cpp.hpp:705
double ceil_impl_double(double value) FL_NO_EXCEPT
Definition math.cpp.hpp:71
double fabs_impl_double(double value) FL_NO_EXCEPT
Definition math.cpp.hpp:441
u8 u8 fract8 blur_amount FL_NO_EXCEPT
Definition blur.h:186
constexpr int type_rank< T >::value
long lround_impl_float(float value) FL_NO_EXCEPT
Definition math.cpp.hpp:445
double ldexp_impl_double(double value, int exp) FL_NO_EXCEPT
Definition math.cpp.hpp:735
double tan_impl_double(double value) FL_NO_EXCEPT
Definition math.cpp.hpp:680
double sin_impl_double(double value) FL_NO_EXCEPT
Definition math.cpp.hpp:346
float sqrt_impl_float(float value) FL_NO_EXCEPT
Definition math.cpp.hpp:322
double fmod_impl_double(double x, double y) FL_NO_EXCEPT
Definition math.cpp.hpp:495
float exp_impl_float(float value) FL_NO_EXCEPT
Definition math.cpp.hpp:201
float floor_impl_float(float value) FL_NO_EXCEPT
Definition math.cpp.hpp:44
double sqrt_impl_double(double value) FL_NO_EXCEPT
Definition math.cpp.hpp:330
double floor_impl_double(double value) FL_NO_EXCEPT
Definition math.cpp.hpp:53
double pow_impl_double(double base, double exponent) FL_NO_EXCEPT
Definition math.cpp.hpp:420
double acos_impl_double(double value) FL_NO_EXCEPT
Definition math.cpp.hpp:661
long lround_impl_double(double value) FL_NO_EXCEPT
Definition math.cpp.hpp:454
float atan_impl_float(float value) FL_NO_EXCEPT
Definition math.cpp.hpp:610
float sin_impl_float(float value) FL_NO_EXCEPT
Definition math.cpp.hpp:338
float acos_impl_float(float value) FL_NO_EXCEPT
Definition math.cpp.hpp:652
float pow_impl_float(float base, float exponent) FL_NO_EXCEPT
Definition math.cpp.hpp:404
float hypot_impl_float(float x, float y) FL_NO_EXCEPT
Definition math.cpp.hpp:594
float ceil_impl_float(float value) FL_NO_EXCEPT
Definition math.cpp.hpp:62
double asin_impl_double(double value) FL_NO_EXCEPT
Definition math.cpp.hpp:639
double atan_impl_double(double value) FL_NO_EXCEPT
Definition math.cpp.hpp:618
double cos_impl_double(double value) FL_NO_EXCEPT
Definition math.cpp.hpp:362
float round_impl_float(float value) FL_NO_EXCEPT
Definition math.cpp.hpp:466
float log_impl_float(float value) FL_NO_EXCEPT
Definition math.cpp.hpp:370
float log10_impl_float(float value) FL_NO_EXCEPT
Definition math.cpp.hpp:386
enable_if< is_fixed_point< T >::value, T >::type exp(T x) FL_NO_EXCEPT
double round_impl_double(double value) FL_NO_EXCEPT
Definition math.cpp.hpp:474
double log_impl_double(double value) FL_NO_EXCEPT
Definition math.cpp.hpp:378
float ldexp_impl_float(float value, int exp) FL_NO_EXCEPT
Definition math.cpp.hpp:727
double atan2_impl_double(double y, double x) FL_NO_EXCEPT
Definition math.cpp.hpp:586
double hypot_impl_double(double x, double y) FL_NO_EXCEPT
Definition math.cpp.hpp:602
float tan_impl_float(float value) FL_NO_EXCEPT
Definition math.cpp.hpp:670
float asin_impl_float(float value) FL_NO_EXCEPT
Definition math.cpp.hpp:626
double exp_impl_double(double value) FL_NO_EXCEPT
Definition math.cpp.hpp:202
double log10_impl_double(double value) FL_NO_EXCEPT
Definition math.cpp.hpp:395
float fabs_impl_float(float value) FL_NO_EXCEPT
Definition math.cpp.hpp:437
float cos_impl_float(float value) FL_NO_EXCEPT
Definition math.cpp.hpp:354
float atan2_impl_float(float y, float x) FL_NO_EXCEPT
Definition math.cpp.hpp:578
float fmod_impl_float(float x, float y) FL_NO_EXCEPT
Definition math.cpp.hpp:483
Base definition for an LED controller.
Definition crgb.hpp:179
fl::u64 u64
Definition stdint.h:220
fl::i64 i64
Definition stdint.h:221