FastLED 3.9.15
Loading...
Searching...
No Matches
traits.h
Go to the documentation of this file.
1#pragma once
2
3// Type traits for fixed-point arithmetic.
4// Computes raw types, intermediate precision, and constants based on IntBits/FracBits.
5
6#include "fl/stl/stdint.h"
8#include "fl/stl/noexcept.h"
10
11namespace fl {
12
13namespace detail {
14
15// True when T is a non-bool integer type. Used by every fixed-point
16// integer constructor so they bind to any width (portable across AVR
17// 16-bit int and 32-bit platforms).
18template <typename T>
24
25// SFINAE helper — drop into a template parameter list:
26// template <typename IntT, detail::enable_if_integer_t<IntT> = 0>
27template <typename T>
30
31// Not constexpr: calling from a constexpr context triggers a compile error.
32// This is the C++11 "constexpr assert" pattern.
34
35// Range check: is n in [0, MAX_U]? Works for both signed and unsigned IntT.
36// For signed IntT, explicitly rejects negative values via i32 comparison.
37// For unsigned IntT, the negativity check is always true (optimized away).
38template <typename IntT>
39constexpr bool in_unsigned_range(IntT n, u32 max_u) FL_NOEXCEPT {
40 return static_cast<i32>(fl::is_signed<IntT>::value ? (n >= IntT(0)) : 1) &&
41 static_cast<u32>(n) <= max_u;
42}
43
44// Checked integer-to-raw conversion for fixed-point constructors.
45// Verifies n fits in INT_BITS at constexpr time; fires a compile error if not.
46// Two specializations: 16-bit storage (i16/u16) and 32-bit storage (i32/u32).
47template <int IntBits, int FracBits, bool TotalLE16 = (IntBits + FracBits <= 16)>
48struct int_to_fixed;
49
50// 16-bit storage types (s12x4, s8x8, s4x12, u12x4, u8x8, u4x12).
51// Intermediate multiply in i32/u32 (one step wider).
52template <int IntBits, int FracBits>
54 static constexpr i32 SCALE = static_cast<i32>(1) << FracBits;
55 static constexpr i32 MAX_S = (static_cast<i32>(1) << (IntBits - 1)) - 1;
56 static constexpr i32 MIN_S = -(static_cast<i32>(1) << (IntBits - 1));
57 static constexpr u32 MAX_U = (static_cast<u32>(1) << IntBits) - 1;
58
59 template <typename IntT>
60 static constexpr i16 from_signed(IntT n) FL_NOEXCEPT {
61 return (static_cast<i32>(n) >= MIN_S && static_cast<i32>(n) <= MAX_S)
62 ? static_cast<i16>(static_cast<i32>(n) * SCALE)
64 }
65
66 template <typename IntT>
67 static constexpr u16 from_unsigned(IntT n) FL_NOEXCEPT {
68 return in_unsigned_range(n, MAX_U)
69 ? static_cast<u16>(static_cast<u32>(n) * SCALE)
71 }
72};
73
74// 32-bit storage types (s16x16, s24x8, s8x24, u16x16, u24x8, u8x24).
75// Signed uses unsigned multiply to avoid signed overflow UB.
76template <int IntBits, int FracBits>
78 static constexpr u32 USCALE = static_cast<u32>(static_cast<i32>(1) << FracBits);
79 static constexpr i32 MAX_S = (static_cast<i32>(1) << (IntBits - 1)) - 1;
80 static constexpr i32 MIN_S = -(static_cast<i32>(1) << (IntBits - 1));
81 static constexpr u32 MAX_U = (static_cast<u32>(1) << IntBits) - 1;
82
83 template <typename IntT>
84 static constexpr i32 from_signed(IntT n) FL_NOEXCEPT {
85 return (static_cast<i32>(n) >= MIN_S && static_cast<i32>(n) <= MAX_S)
86 ? static_cast<i32>(static_cast<u32>(n) * USCALE)
88 }
89
90 template <typename IntT>
91 static constexpr u32 from_unsigned(IntT n) FL_NOEXCEPT {
92 return in_unsigned_range(n, MAX_U)
93 ? static_cast<u32>(n) * USCALE
95 }
96};
97
98} // namespace detail
99
100template <int IntBits, int FracBits>
102 // Total bits required
103 static constexpr int TOTAL_BITS = IntBits + FracBits;
104
105 // Raw storage type: i16 if fits in 16 bits, else i32
106 using raw_type = fl::conditional_t<(TOTAL_BITS <= 16), i16, i32>;
107 using unsigned_raw_type = fl::conditional_t<(TOTAL_BITS <= 16), u16, u32>;
108
109 // Intermediate types for multiplication/division (needs double width)
110 using intermediate_type = fl::conditional_t<(TOTAL_BITS <= 16), i32, i64>;
111 using unsigned_intermediate_type = fl::conditional_t<(TOTAL_BITS <= 16), u32, u64>;
112
113 // Intermediate fractional precision for log2/exp2 polynomial evaluation
114 // Pattern: extend FRAC_BITS to higher precision to minimize rounding error
115 // - FRAC >= 24: IFRAC = FRAC (already max precision, no extension)
116 // - FRAC >= 16: IFRAC = 24 (extend to 24 bits)
117 // - FRAC >= 12: IFRAC = 20 (extend to 20 bits)
118 // - FRAC >= 8: IFRAC = 16 (extend to 16 bits)
119 // - FRAC < 8: IFRAC = 12 (minimum reasonable precision)
120 static constexpr int IFRAC =
121 (FracBits >= 24) ? FracBits :
122 (FracBits >= 16) ? 24 :
123 (FracBits >= 12) ? 20 :
124 (FracBits >= 8) ? 16 : 12;
125
126 // Intermediate type for polynomial evaluation (i32 or i64 based on IFRAC)
127 using poly_intermediate_type = fl::conditional_t<(IFRAC <= 16), i32, i64>;
128
129 // Maximum overflow value for exp2_fp saturation
130 static constexpr auto MAX_OVERFLOW =
131 (TOTAL_BITS <= 16) ? static_cast<raw_type>(0x7FFF)
132 : static_cast<raw_type>(0x7FFFFFFF);
133
134 // Sin/cos shift amount: sin32/cos32 output is i32 with 31 fractional bits
135 // We shift right by (31 - FRAC_BITS) to get FRAC_BITS precision
136 static constexpr int SIN_COS_SHIFT = 31 - FracBits;
137
138 // Sqrt function selection: use isqrt32 for i16, isqrt64 for i32
139 static constexpr bool USE_ISQRT32 = (TOTAL_BITS <= 16);
140
141 // Compile-time overflow safety checks
142 // Verify intermediate_type can hold worst-case products for operator*
143 // raw_max * raw_max needs 2*TOTAL_BITS bits
145 "intermediate_type too narrow for operator* overflow safety");
146
147 // Verify poly_intermediate_type can hold polynomial products
148 // For IFRAC <= 16: acc * t_ifrac fits in i32 after >> IFRAC (widened to i64 for multiply)
149 // For IFRAC > 16: acc * t_ifrac needs i64 throughout
150 // The assertion checks we have enough bits for the intermediate product before shift
151 // Max product: ~1.5 * 2^IFRAC * 2^IFRAC = ~1.5 * 2^(2*IFRAC)
152 // For i32: needs 2*IFRAC + 1 <= 31, so IFRAC <= 15 (but we use i64 for multiply, safe)
153 // For i64: needs 2*IFRAC + 1 <= 63, so IFRAC <= 31 (all our types fit)
154 FL_STATIC_ASSERT((IFRAC <= 16 && sizeof(poly_intermediate_type) * 8 >= 32) ||
155 (IFRAC > 16 && sizeof(poly_intermediate_type) * 8 >= 64),
156 "poly_intermediate_type insufficient for polynomial evaluation");
157};
158
159} // namespace fl
void integer_out_of_range_for_fixed_point_type() FL_NOEXCEPT
Definition traits.h:33
typename fl::enable_if< is_non_bool_integer< T >::value, int >::type enable_if_integer_t
Definition traits.h:28
constexpr bool in_unsigned_range(IntT n, u32 max_u) FL_NOEXCEPT
Definition traits.h:39
Compile-time linker keep-alive hook for a single fl::Bus.
Definition bus_traits.h:48
typename conditional< B, T, F >::type conditional_t
Definition s16x16x4.h:115
Base definition for an LED controller.
Definition crgb.hpp:179
#define FL_NOEXCEPT
Portable compile-time assertion wrapper.
static constexpr u32 from_unsigned(IntT n) FL_NOEXCEPT
Definition traits.h:91
static constexpr i32 from_signed(IntT n) FL_NOEXCEPT
Definition traits.h:84
static constexpr u16 from_unsigned(IntT n) FL_NOEXCEPT
Definition traits.h:67
static constexpr i16 from_signed(IntT n) FL_NOEXCEPT
Definition traits.h:60
static constexpr bool value
Definition traits.h:20
fl::conditional_t<(TOTAL_BITS<=16), i16, i32 > raw_type
Definition traits.h:106
static constexpr int SIN_COS_SHIFT
Definition traits.h:136
fl::conditional_t<(IFRAC<=16), i32, i64 > poly_intermediate_type
Definition traits.h:127
static constexpr bool USE_ISQRT32
Definition traits.h:139
static constexpr auto MAX_OVERFLOW
Definition traits.h:130
FL_STATIC_ASSERT(sizeof(intermediate_type) *8 >=TOTAL_BITS *2, "intermediate_type too narrow for operator* overflow safety")
fl::conditional_t<(TOTAL_BITS<=16), u16, u32 > unsigned_raw_type
Definition traits.h:107
static constexpr int IFRAC
Definition traits.h:120
FL_STATIC_ASSERT((IFRAC<=16 &&sizeof(poly_intermediate_type) *8 >=32)||(IFRAC > 16 &&sizeof(poly_intermediate_type) *8 >=64), "poly_intermediate_type insufficient for polynomial evaluation")
fl::conditional_t<(TOTAL_BITS<=16), i32, i64 > intermediate_type
Definition traits.h:110
static constexpr int TOTAL_BITS
Definition traits.h:103
fl::conditional_t<(TOTAL_BITS<=16), u32, u64 > unsigned_intermediate_type
Definition traits.h:111