FastLED 3.9.15
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// IWYU pragma: end_keep // okay banned header (STL wrapper implementation requires standard math.h)
27#endif
28
29// Arduino's WString.h (transitively included via the Arduino core on AVR and
30// other Arduino-framework targets) defines `F()` as a PROGMEM-string macro.
31// That macro collides with the `template <typename F>` parameter used by
32// the polynomial / Newton-iteration helpers in this file: every `F(value)`
33// constructor cast (e.g. `F(0.5)`) is rewritten to `WString::F(0.5)` and
34// fails to compile. Undefining the macro here is local to this translation
35// unit; the macro is only used in user sketches, never in fl::math itself.
36#ifdef F
37#undef F
38#endif
39
40namespace fl {
41
42// Standalone floor implementation for float
43float floor_impl_float(float value) {
44 if (value >= 0.0f) {
45 return static_cast<float>(static_cast<int>(value));
46 }
47 int i = static_cast<int>(value);
48 return static_cast<float>(i - (value != static_cast<float>(i) ? 1 : 0));
49}
50
51// Standalone floor implementation for double
52double floor_impl_double(double value) {
53 if (value >= 0.0) {
54 return static_cast<double>(static_cast<long long>(value));
55 }
56 long long i = static_cast<long long>(value);
57 return static_cast<double>(i - (value != static_cast<double>(i) ? 1 : 0));
58}
59
60// Standalone ceil implementation for float
61float ceil_impl_float(float value) {
62 if (value <= 0.0f) {
63 return static_cast<float>(static_cast<int>(value));
64 }
65 int i = static_cast<int>(value);
66 return static_cast<float>(i + (value != static_cast<float>(i) ? 1 : 0));
67}
68
69// Standalone ceil implementation for double
70double ceil_impl_double(double value) {
71 if (value <= 0.0) {
72 return static_cast<double>(static_cast<long long>(value));
73 }
74 long long i = static_cast<long long>(value);
75 return static_cast<double>(i + (value != static_cast<double>(i) ? 1 : 0));
76}
77
78// Standalone exp implementation using Taylor series
79// e^x ≈ 1 + x + x²/2! + x³/3! + x⁴/4! + ...
80float exp_impl_float(float value) {
81 if (value > 10.0f)
82 return 22026.465794806718f; // e^10 approx
83 if (value < -10.0f)
84 return 0.0000453999297625f; // e^-10 approx
85
86 // For negative values, use exp(x) = 1/exp(-x) to keep the Taylor series
87 // input non-negative where it converges well with limited terms.
88 if (value < 0.0f) {
89 return 1.0f / exp_impl_float(-value);
90 }
91
92 float result = 1.0f;
93 float term = 1.0f;
94 for (int i = 1; i < 10; ++i) {
95 term *= value / static_cast<float>(i);
96 result += term;
97 }
98 return result;
99}
100
101double exp_impl_double(double value) {
102 if (value > 10.0)
103 return 22026.465794806718; // e^10 approx
104 if (value < -10.0)
105 return 0.0000453999297625; // e^-10 approx
106
107 // For negative values, use exp(x) = 1/exp(-x) to keep the Taylor series
108 // input non-negative where it converges well with limited terms.
109 if (value < 0.0) {
110 return 1.0 / exp_impl_double(-value);
111 }
112
113 double result = 1.0;
114 double term = 1.0;
115 for (int i = 1; i < 10; ++i) {
116 term *= value / static_cast<double>(i);
117 result += term;
118 }
119 return result;
120}
121
122// =============================================================================
123// Libm-free trig / sqrt / log / pow implementations
124// =============================================================================
125//
126// Hand-rolled to keep fl::math from anchoring libm in the link on freestanding
127// targets. Previously the wrappers below all called `::sqrtf` / `::sin` /
128// `::log` / etc, which pulled libm into every sketch that touched fl::math —
129// transitively dragging in libgcc's full soft-double helper set (`__aeabi_dadd`,
130// `__aeabi_dmul`, `__aeabi_ddiv`, ...) on no-FPU MCUs. On the LPC845-BRK
131// JSON-RPC bring-up sketch (#3002), that was ~6 KB of overhead — more than
132// 10 % of the entire 64 KB flash budget — for math operations that the sketch
133// never actually performed.
134//
135// Strategy: polynomial / Newton iteration approximations with range reduction.
136// Accuracy target is FastLED's animation use cases (color wheel angles,
137// particle orientation, easing curves, gamma LUT generation) — typically
138// ~3-4 decimal digits relative is plenty. Sketches that need full IEEE-754
139// precision can call `<cmath>` directly; doing so anchors libm at THAT call
140// site only, which is the explicit cost the user opted into.
141
142namespace detail {
143
144// --- sqrt via Newton-Raphson iteration ------------------------------------
145//
146// Initial estimate from the IEEE-754 exponent halving trick (well-known
147// "bit-hack" approach, refined). Two Newton iterations converge to roughly
148// 5 decimal digits for float, 10 for double — more than enough for color
149// space and rotational math.
150template <typename F>
152 if (value <= F(0)) return F(0);
153 // Initial estimate: x_0 = value / 2. Pure-arithmetic, no bit-cast needed.
154 // Newton converges quadratically so 5-6 iterations is plenty even from a
155 // crude start.
156 F x = value;
157 if (x > F(1)) x = F(1) + (x - F(1)) * F(0.5); // bias toward 1 for fast convergence
158 for (int i = 0; i < 6; ++i) {
159 if (x == F(0)) break;
160 x = F(0.5) * (x + value / x);
161 }
162 return x;
163}
164
165// --- Polynomial sin/cos with range reduction ------------------------------
166//
167// Reduce input to [-π/4, π/4] then evaluate degree-7 Taylor polynomial.
168// Max error ~1e-5 for float, ~1e-10 for double on the reduced range.
169template <typename F>
171 // sin(x) ≈ x - x³/6 + x⁵/120 - x⁷/5040
172 const F x2 = x * x;
173 return x * (F(1) - x2 * (F(1.0 / 6.0) - x2 * (F(1.0 / 120.0) - x2 * F(1.0 / 5040.0))));
174}
175
176template <typename F>
178 // cos(x) ≈ 1 - x²/2 + x⁴/24 - x⁶/720
179 const F x2 = x * x;
180 return F(1) - x2 * (F(0.5) - x2 * (F(1.0 / 24.0) - x2 * F(1.0 / 720.0)));
181}
182
183template <typename F>
185 const F kPi = F(3.14159265358979323846);
186 const F kTwoPi = F(6.28318530717958647692);
187 const F kPiHalf = F(1.57079632679489661923);
188 // Reduce to [-π, π]: subtract floor(x / 2π) * 2π.
189 // For the FastLED use cases we expect |x| < ~100, so this loop runs a
190 // bounded number of times. For pathological inputs we cap iterations.
191 int guard = 32;
192 while (x > kPi && guard > 0) { x -= kTwoPi; --guard; }
193 while (x < -kPi && guard > 0) { x += kTwoPi; --guard; }
194 // Map to [-π/2, π/2] using sin(π - x) = sin(x).
195 if (x > kPiHalf) x = kPi - x;
196 if (x < -kPiHalf) x = -kPi - x;
197 // [-π/2, π/2] -- the polynomial works fine here (max |x| ≈ 1.57).
198 return sin_poly_quarterturn_(x);
199}
200
201template <typename F>
203 // cos(x) = sin(π/2 - x)
204 const F kPiHalf = F(1.57079632679489661923);
205 return sin_reduce_(kPiHalf - x);
206}
207
208// --- log via fraction extraction + polynomial -----------------------------
209//
210// log(x) = log(m * 2^e) = log(m) + e * log(2) where m ∈ [1, 2)
211// Repeatedly multiply/divide by 2 to bring x into [1, 2), then evaluate
212// log(m) via a polynomial in (m-1) on [0, 1].
213template <typename F>
215 if (value <= F(0)) return F(-1e30); // -inf surrogate
216 int e = 0;
217 while (value >= F(2)) { value *= F(0.5); ++e; }
218 while (value < F(1)) { value *= F(2); --e; }
219 // value ∈ [1, 2); evaluate log(value) via Taylor around 1: let u = value - 1.
220 // log(1+u) = u - u²/2 + u³/3 - u⁴/4 + ...
221 // Use enough terms for ~5-decimal accuracy on [0, 1].
222 const F u = value - F(1);
223 const F u2 = u * u;
224 const F u3 = u2 * u;
225 const F u4 = u2 * u2;
226 const F u5 = u4 * u;
227 const F u6 = u4 * u2;
228 const F u7 = u6 * u;
229 F log_m = u - u2 * F(0.5) + u3 * F(1.0 / 3.0) - u4 * F(0.25)
230 + u5 * F(0.2) - u6 * F(1.0 / 6.0) + u7 * F(1.0 / 7.0);
231 const F kLn2 = F(0.69314718055994530942);
232 return log_m + F(e) * kLn2;
233}
234
235} // namespace detail
236
237// Each impl below picks libm (Large-memory targets) or polynomial / Newton
238// (Low-memory targets — keeps libm out of the link).
239
240float sqrt_impl_float(float value) {
241#if FL_MATH_USE_LIBM
242 return ::sqrtf(value);
243#else
245#endif
246}
247
248double sqrt_impl_double(double value) {
249#if FL_MATH_USE_LIBM
250 return ::sqrt(value);
251#else
253#endif
254}
255
256float sin_impl_float(float value) {
257#if FL_MATH_USE_LIBM
258 return ::sinf(value);
259#else
261#endif
262}
263
264double sin_impl_double(double value) {
265#if FL_MATH_USE_LIBM
266 return ::sin(value);
267#else
269#endif
270}
271
272float cos_impl_float(float value) {
273#if FL_MATH_USE_LIBM
274 return ::cosf(value);
275#else
277#endif
278}
279
280double cos_impl_double(double value) {
281#if FL_MATH_USE_LIBM
282 return ::cos(value);
283#else
285#endif
286}
287
288float log_impl_float(float value) {
289#if FL_MATH_USE_LIBM
290 return ::logf(value);
291#else
293#endif
294}
295
296double log_impl_double(double value) {
297#if FL_MATH_USE_LIBM
298 return ::log(value);
299#else
301#endif
302}
303
305#if FL_MATH_USE_LIBM
306 return ::log10f(value);
307#else
308 const float kInvLn10 = 0.43429448190325182765f;
309 return detail::log_natural_<float>(value) * kInvLn10;
310#endif
311}
312
313double log10_impl_double(double value) {
314#if FL_MATH_USE_LIBM
315 return ::log10(value);
316#else
317 const double kInvLn10 = 0.43429448190325182765;
318 return detail::log_natural_<double>(value) * kInvLn10;
319#endif
320}
321
322float pow_impl_float(float base, float exponent) {
323#if FL_MATH_USE_LIBM
324 return ::powf(base, exponent);
325#else
326 if (exponent == 0.0f) return 1.0f;
327 int ie = static_cast<int>(exponent);
328 if (static_cast<float>(ie) == exponent && ie >= -8 && ie <= 8) {
329 float r = 1.0f;
330 if (ie > 0) { for (int i = 0; i < ie; ++i) r *= base; return r; }
331 if (ie < 0) { for (int i = 0; i < -ie; ++i) r *= base; return 1.0f / r; }
332 }
333 if (base <= 0.0f) return 0.0f;
334 return exp_impl_float(exponent * detail::log_natural_<float>(base));
335#endif
336}
337
338double pow_impl_double(double base, double exponent) {
339#if FL_MATH_USE_LIBM
340 return ::pow(base, exponent);
341#else
342 if (exponent == 0.0) return 1.0;
343 int ie = static_cast<int>(exponent);
344 if (static_cast<double>(ie) == exponent && ie >= -8 && ie <= 8) {
345 double r = 1.0;
346 if (ie > 0) { for (int i = 0; i < ie; ++i) r *= base; return r; }
347 if (ie < 0) { for (int i = 0; i < -ie; ++i) r *= base; return 1.0 / r; }
348 }
349 if (base <= 0.0) return 0.0;
350 return exp_impl_double(exponent * detail::log_natural_<double>(base));
351#endif
352}
353
354// Absolute value: pure arithmetic; libm path unnecessary, same on both.
355float fabs_impl_float(float value) {
356 return value < 0.0f ? -value : value;
357}
358
359double fabs_impl_double(double value) {
360 return value < 0.0 ? -value : value;
361}
362
364#if FL_MATH_USE_LIBM
365 return ::lroundf(value);
366#else
367 if (value >= 0.0f) return static_cast<long>(value + 0.5f);
368 return static_cast<long>(value - 0.5f);
369#endif
370}
371
373#if FL_MATH_USE_LIBM
374 return ::lround(value);
375#else
376 if (value >= 0.0) return static_cast<long>(value + 0.5);
377 return static_cast<long>(value - 0.5);
378#endif
379}
380
381// Arduino.h defines `round` as a macro, so we temporarily hide it.
382#pragma push_macro("round")
383#undef round
385#if FL_MATH_USE_LIBM
386 return ::roundf(value);
387#else
388 return static_cast<float>(lround_impl_float(value));
389#endif
390}
391
392double round_impl_double(double value) {
393#if FL_MATH_USE_LIBM
394 return ::round(value);
395#else
396 return static_cast<double>(lround_impl_double(value));
397#endif
398}
399#pragma pop_macro("round")
400
401float fmod_impl_float(float x, float y) {
402#if FL_MATH_USE_LIBM
403 return ::fmodf(x, y);
404#else
405 if (y == 0.0f) return 0.0f;
406 const float q = x / y;
407 // Truncate toward zero (libm fmod convention).
408 const float t = q >= 0.0f ? floor_impl_float(q) : ceil_impl_float(q);
409 return x - t * y;
410#endif
411}
412
413double fmod_impl_double(double x, double y) {
414#if FL_MATH_USE_LIBM
415 return ::fmod(x, y);
416#else
417 if (y == 0.0) return 0.0;
418 const double q = x / y;
419 const double t = q >= 0.0 ? floor_impl_double(q) : ceil_impl_double(q);
420 return x - t * y;
421#endif
422}
423
424// Inverse tangent 2 implementations (atan2).
425//
426// Hand-rolled to avoid pulling libm into the link on freestanding targets
427// (Cortex-M0+, AVR, etc). On the LPC845-BRK bring-up firmware (#3002), this
428// wrapper used to anchor `::atan2`, which transitively pulled in libm's
429// `s_scalbn` / `w_fmod` / `w_acos` and from there the full libgcc soft-double
430// helper set (`__aeabi_dadd`, `__aeabi_dmul`, `__aeabi_ddiv`, ...) — together
431// ~6 KB of pure overhead on a 64 KB-flash part for a sketch that never
432// actually computes an arctangent.
433//
434// Implementation: piecewise minimax polynomial on |y/x| in [0, 1] (degree-5),
435// extended to full [-π, π] via the standard quadrant + reciprocal identities:
436//
437// atan2(y, x) = atan(y/x) for x > 0
438// = atan(y/x) + π for x < 0, y >= 0
439// = atan(y/x) - π for x < 0, y < 0
440// = ±π/2 for x == 0 (sign of y)
441//
442// atan(u) = polynomial(u) for |u| <= 1
443// = π/2 - atan(1/u) for u > 1
444// = -π/2 - atan(1/u) for u < -1
445//
446// The degree-5 polynomial coefficients are Chebyshev-fit on [-1, 1]; max
447// error ≈ 1.5e-3 rad (~0.085°). Adequate for FastLED's animation and
448// rotational-math use cases — color wheel angles, particle orientation,
449// xypath rotations. Sketches that need full double precision should bypass
450// fl::math and call libm directly via `<cmath>` so they get the libm pull
451// only when they actually need it.
452namespace detail {
453template <typename F>
455 // Polynomial approximation of atan on [-1, 1].
456 // Coefficients from Padé-style minimax fit; max abs error ~1.5e-3.
457 const F u2 = u * u;
458 return u * (F(0.99997726) +
459 u2 * (F(-0.33262347) +
460 u2 * (F(0.19354346) +
461 u2 * (F(-0.11643287) +
462 u2 * (F(0.05265332) +
463 u2 * F(-0.01172120))))));
464}
465
466template <typename F>
467inline F atan_full_(F u) FL_NOEXCEPT {
468 const F kPiHalf = F(1.57079632679489661923);
469 // Reduce to |u| <= 1 via the reciprocal identity.
470 if (u > F(1)) {
471 return kPiHalf - atan_poly_unit_(F(1) / u);
472 } else if (u < F(-1)) {
473 return -kPiHalf - atan_poly_unit_(F(1) / u);
474 }
475 return atan_poly_unit_(u);
476}
477
478template <typename F>
479inline F atan2_full_(F y, F x) FL_NOEXCEPT {
480 const F kPi = F(3.14159265358979323846);
481 const F kPiHalf = F(1.57079632679489661923);
482 // x == 0 special case: angle is ±π/2 (sign of y), or 0 when both are 0.
483 if (x == F(0)) {
484 if (y > F(0)) return kPiHalf;
485 if (y < F(0)) return -kPiHalf;
486 return F(0);
487 }
488 F a = atan_full_(y / x);
489 if (x < F(0)) {
490 a += (y >= F(0)) ? kPi : -kPi;
491 }
492 return a;
493}
494} // namespace detail
495
496float atan2_impl_float(float y, float x) {
497#if FL_MATH_USE_LIBM
498 return ::atan2f(y, x);
499#else
501#endif
502}
503
504double atan2_impl_double(double y, double x) {
505#if FL_MATH_USE_LIBM
506 return ::atan2(y, x);
507#else
509#endif
510}
511
512float hypot_impl_float(float x, float y) {
513#if FL_MATH_USE_LIBM
514 return ::hypotf(x, y);
515#else
516 return detail::sqrt_newton_<float>(x * x + y * y);
517#endif
518}
519
520double hypot_impl_double(double x, double y) {
521#if FL_MATH_USE_LIBM
522 return ::hypot(x, y);
523#else
524 return detail::sqrt_newton_<double>(x * x + y * y);
525#endif
526}
527
528float atan_impl_float(float value) {
529#if FL_MATH_USE_LIBM
530 return ::atanf(value);
531#else
533#endif
534}
535
536double atan_impl_double(double value) {
537#if FL_MATH_USE_LIBM
538 return ::atan(value);
539#else
541#endif
542}
543
544float asin_impl_float(float value) {
545#if FL_MATH_USE_LIBM
546 return ::asinf(value);
547#else
548 const float kPiHalf = 1.57079632679489661923f;
549 if (value >= 1.0f) return kPiHalf;
550 if (value <= -1.0f) return -kPiHalf;
551 const float denom = detail::sqrt_newton_<float>(1.0f - value * value);
552 if (denom == 0.0f) return value > 0.0f ? kPiHalf : -kPiHalf;
553 return detail::atan_full_<float>(value / denom);
554#endif
555}
556
557double asin_impl_double(double value) {
558#if FL_MATH_USE_LIBM
559 return ::asin(value);
560#else
561 const double kPiHalf = 1.57079632679489661923;
562 if (value >= 1.0) return kPiHalf;
563 if (value <= -1.0) return -kPiHalf;
564 const double denom = detail::sqrt_newton_<double>(1.0 - value * value);
565 if (denom == 0.0) return value > 0.0 ? kPiHalf : -kPiHalf;
566 return detail::atan_full_<double>(value / denom);
567#endif
568}
569
570float acos_impl_float(float value) {
571#if FL_MATH_USE_LIBM
572 return ::acosf(value);
573#else
574 const float kPiHalf = 1.57079632679489661923f;
575 return kPiHalf - asin_impl_float(value);
576#endif
577}
578
579double acos_impl_double(double value) {
580#if FL_MATH_USE_LIBM
581 return ::acos(value);
582#else
583 const double kPiHalf = 1.57079632679489661923;
584 return kPiHalf - asin_impl_double(value);
585#endif
586}
587
588float tan_impl_float(float value) {
589#if FL_MATH_USE_LIBM
590 return ::tanf(value);
591#else
592 const float c = detail::cos_reduce_<float>(value);
593 if (c == 0.0f) return 0.0f;
595#endif
596}
597
598double tan_impl_double(double value) {
599#if FL_MATH_USE_LIBM
600 return ::tan(value);
601#else
602 const double c = detail::cos_reduce_<double>(value);
603 if (c == 0.0) return 0.0;
605#endif
606}
607
608// Load exponent implementations (ldexp): value * 2^exp.
609//
610// Hand-rolled (no libm) for the same reason as atan2 above — `::ldexp` /
611// `::ldexpf` was the other observed external libm entry from `fl.math+` in
612// the LPC845-BRK bring-up firmware map.
613//
614// Strategy: scale by repeated doubling/halving rather than touching IEEE-754
615// exponent bits directly. This is portable (no bit_cast assumptions, no
616// endianness dependence, no signaling-NaN risk) and the worst-case loop
617// count is bounded by the float/double exponent range (~127 / ~1023). For
618// the FastLED use cases that anchor this symbol — stb_vorbis decoder,
619// fixed_point's to_float(), animartrix rotational math — exp is typically
620// a small integer (|exp| < 30), so the bounded loop is cheap (≤ 30 mults).
621namespace detail {
622template <typename F>
624 if (value == F(0)) return value;
625 if (exp == 0) return value;
626 if (exp > 0) {
627 // Cap to avoid pathological loops; saturate to ±inf-ish via overflow
628 // (these magnitudes are well outside the IEEE 754 finite range, and
629 // returning a saturated value matches libm's behavior of producing
630 // ±HUGE_VAL on overflow).
631 if (exp > 1024) exp = 1024;
632 while (exp >= 30) { value *= F(1073741824); exp -= 30; } // 2^30
633 while (exp > 0) { value *= F(2); --exp; }
634 return value;
635 }
636 // exp < 0
637 int neg = -exp;
638 if (neg > 1024) neg = 1024;
639 while (neg >= 30) { value *= F(1.0 / 1073741824.0); neg -= 30; }
640 while (neg > 0) { value *= F(0.5); --neg; }
641 return value;
642}
643} // namespace detail
644
645float ldexp_impl_float(float value, int exp) {
646#if FL_MATH_USE_LIBM
647 return ::ldexpf(value, exp);
648#else
650#endif
651}
652
653double ldexp_impl_double(double value, int exp) {
654#if FL_MATH_USE_LIBM
655 return ::ldexp(value, exp);
656#else
658#endif
659}
660
661} // namespace fl
F atan_poly_unit_(F u) FL_NOEXCEPT
Definition math.cpp.hpp:454
F sin_reduce_(F x) FL_NOEXCEPT
Definition math.cpp.hpp:184
F atan_full_(F u) FL_NOEXCEPT
Definition math.cpp.hpp:467
F sin_poly_quarterturn_(F x) FL_NOEXCEPT
Definition math.cpp.hpp:170
F atan2_full_(F y, F x) FL_NOEXCEPT
Definition math.cpp.hpp:479
F cos_reduce_(F x) FL_NOEXCEPT
Definition math.cpp.hpp:202
F ldexp_loop_(F value, int exp) FL_NOEXCEPT
Definition math.cpp.hpp:623
F log_natural_(F value) FL_NOEXCEPT
Definition math.cpp.hpp:214
F sqrt_newton_(F value) FL_NOEXCEPT
Definition math.cpp.hpp:151
F cos_poly_quarterturn_(F x) FL_NOEXCEPT
Definition math.cpp.hpp:177
double hypot_impl_double(double x, double y)
Definition math.cpp.hpp:520
double atan2_impl_double(double y, double x)
Definition math.cpp.hpp:504
float log_impl_float(float value)
Definition math.cpp.hpp:288
float fabs_impl_float(float value)
Definition math.cpp.hpp:355
constexpr int type_rank< T >::value
double sqrt_impl_double(double value)
Definition math.cpp.hpp:248
double atan_impl_double(double value)
Definition math.cpp.hpp:536
double cos_impl_double(double value)
Definition math.cpp.hpp:280
float exp_impl_float(float value)
Definition math.cpp.hpp:80
double pow_impl_double(double base, double exponent)
Definition math.cpp.hpp:338
float ceil_impl_float(float value)
Definition math.cpp.hpp:61
double tan_impl_double(double value)
Definition math.cpp.hpp:598
double sin_impl_double(double value)
Definition math.cpp.hpp:264
double acos_impl_double(double value)
Definition math.cpp.hpp:579
float fmod_impl_float(float x, float y)
Definition math.cpp.hpp:401
float log10_impl_float(float value)
Definition math.cpp.hpp:304
double ldexp_impl_double(double value, int exp)
Definition math.cpp.hpp:653
float atan2_impl_float(float y, float x)
Definition math.cpp.hpp:496
double fabs_impl_double(double value)
Definition math.cpp.hpp:359
double log_impl_double(double value)
Definition math.cpp.hpp:296
double floor_impl_double(double value)
Definition math.cpp.hpp:52
float acos_impl_float(float value)
Definition math.cpp.hpp:570
double log10_impl_double(double value)
Definition math.cpp.hpp:313
float round_impl_float(float value)
Definition math.cpp.hpp:384
float cos_impl_float(float value)
Definition math.cpp.hpp:272
expected< T, E > result
Alias for expected (Rust-style naming)
Definition result.h:31
double round_impl_double(double value)
Definition math.cpp.hpp:392
float pow_impl_float(float base, float exponent)
Definition math.cpp.hpp:322
long lround_impl_float(float value)
Definition math.cpp.hpp:363
float hypot_impl_float(float x, float y)
Definition math.cpp.hpp:512
float sqrt_impl_float(float value)
Definition math.cpp.hpp:240
double ceil_impl_double(double value)
Definition math.cpp.hpp:70
float tan_impl_float(float value)
Definition math.cpp.hpp:588
double fmod_impl_double(double x, double y)
Definition math.cpp.hpp:413
float atan_impl_float(float value)
Definition math.cpp.hpp:528
float sin_impl_float(float value)
Definition math.cpp.hpp:256
float floor_impl_float(float value)
Definition math.cpp.hpp:43
long lround_impl_double(double value)
Definition math.cpp.hpp:372
float asin_impl_float(float value)
Definition math.cpp.hpp:544
double asin_impl_double(double value)
Definition math.cpp.hpp:557
enable_if< is_fixed_point< T >::value, T >::type exp(T x) FL_NOEXCEPT
double exp_impl_double(double value)
Definition math.cpp.hpp:101
float ldexp_impl_float(float value, int exp)
Definition math.cpp.hpp:645
Base definition for an LED controller.
Definition crgb.hpp:179
#define FL_NOEXCEPT