FastLED 3.10.6
Loading...
Searching...
No Matches

◆ invert3x3Q16()

bool fl::invert3x3Q16 ( const i32(&) in[3][3],
i32(&) out[3][3] )

Inverse of an s16.16 3x3 matrix, in s16.16, computed without floats.

The float-free half of P9 item 2 (FastLED#4043). buildRgbSolveMatrixQ16 still reaches this through invert3x3; a bind path that never touches float calls it directly.

False on a singular matrix, on a coefficient too large for s16.16, and on any input whose exact cofactors or determinant do not fit an i64. That last case is detected rather than bounded away: a deep-blue emitter at xy = (0.14, 0.03) already has a Z column near 28, so a fixed input limit generous enough to be safe would reject real parts.

Definition at line 212 of file device_solve.cpp.hpp.

212 {
213 const i32 a = in[0][0], b = in[0][1], c = in[0][2];
214 const i32 d = in[1][0], e = in[1][1], f = in[1][2];
215 const i32 g = in[2][0], h = in[2][1], i_ = in[2][2];
216
217 // Cofactors are differences of two Q16 products, so Q32.
218 i64 cof[3][3];
219 if (!cofactorQ32(e, i_, f, h, &cof[0][0]) ||
220 !cofactorQ32(c, h, b, i_, &cof[0][1]) ||
221 !cofactorQ32(b, f, c, e, &cof[0][2]) ||
222 !cofactorQ32(f, g, d, i_, &cof[1][0]) ||
223 !cofactorQ32(a, i_, c, g, &cof[1][1]) ||
224 !cofactorQ32(c, d, a, f, &cof[1][2]) ||
225 !cofactorQ32(d, h, e, g, &cof[2][0]) ||
226 !cofactorQ32(b, g, a, h, &cof[2][1]) ||
227 !cofactorQ32(a, e, b, d, &cof[2][2])) {
228 return false;
229 }
230
231 // Determinant is a Q16 times a Q32, so Q48.
232 const i32 first_row[3] = {a, b, c};
233 i64 determinant = 0;
234 for (int col = 0; col < 3; ++col) {
235 const i64 term_lhs = static_cast<i64>(first_row[col]);
236 if (!productFitsI64(term_lhs, cof[col][0])) {
237 return false;
238 }
239 const i64 term = term_lhs * cof[col][0];
240 if (!sumFitsI64(determinant, term)) {
241 return false;
242 }
243 determinant += term;
244 }
245 if (determinant == 0) {
246 return false;
247 }
248
249 for (int row = 0; row < 3; ++row) {
250 for (int col = 0; col < 3; ++col) {
251 const i64 value = divideCoefficientQ16(cof[row][col], determinant);
252 // The same bound the float path applies to its own result, and
253 // for the same reason: past it the solve's accumulator can
254 // overflow for an in-range XYZ input.
255 const i64 limit = static_cast<i64>(kMaxCoefficient) << 16;
256 if (value > limit || value < -limit) {
257 return false;
258 }
259 out[row][col] = static_cast<i32>(value);
260 }
261 }
262 return true;
263}
bool sumFitsI64(i64 a, i64 b) FL_NO_EXCEPT
Does a + b fit an i64, for operands already known to be in range?
bool productFitsI64(i64 a, i64 b) FL_NO_EXCEPT
Does a * b fit an i64?
i64 divideCoefficientQ16(i64 cofactor_q32, i64 determinant_q48) FL_NO_EXCEPT
round(cofactor_q32 * 2^32 / determinant_q48), in s16.16, without a 128-bit intermediate.
constexpr i32 kMaxCoefficient
Largest coefficient magnitude the build accepts, in whole units.
constexpr int type_rank< T >::value
InputGamut g
Definition rgbw.h:124
fl::i64 i64
Definition stdint.h:221

References FL_NO_EXCEPT, g, and type_rank< T >::value.

Referenced by buildRgbSolveMatrixFromQ16(), and buildSourceMatrixQ16().

+ Here is the caller graph for this function: