On first glance, most people would say that GCC is simply wrong. However, the problem does appear to be real. [[ and I’m not talking about the problems inherent with floating point math (NaN, +infinity, -infinity, etc.) ]]
PR 1902 resulted in additional build warnings.
wled00/colors.cpp: In function 'void colorHSVtoRGB(float, float, float, byte&, byte&, byte&)':
wled00/colors.cpp:78:33: warning: 'b' may be used uninitialized in this function [-Wmaybe-uninitialized]
blue = static_cast<uint8_t>(b * 255);
^
wled00/colors.cpp:77:34: warning: 'g' may be used uninitialized in this function [-Wmaybe-uninitialized]
green = static_cast<uint8_t>(g * 255);
^
wled00/colors.cpp:76:32: warning: 'r' may be used uninitialized in this function [-Wmaybe-uninitialized]
red = static_cast<uint8_t>(r * 255);
Here's the relevant code
void colorHSVtoRGB(float hue, float saturation, float value, byte& red, byte& green, byte& blue)
{
float r, g, b;
auto i = static_cast<int>(hue * 6);
auto f = hue * 6 - i;
auto p = value * (1 - saturation);
auto q = value * (1 - f * saturation);
auto t = value * (1 - (1 - f) * saturation);
switch (i % 6)
{
case 0: r = value, g = t, b = p; break;
case 1: r = q, g = value, b = p; break;
case 2: r = p, g = value, b = t; break;
case 3: r = p, g = q, b = value; break;
case 4: r = t, g = p, b = value; break;
case 5: r = value, g = p, b = q; break;
}
red = static_cast<uint8_t>(r * 255);
green = static_cast<uint8_t>(g * 255);
blue = static_cast<uint8_t>(b * 255);
}
The remainder operator (%) can return negative values
See modulo - Modulus with negative numbers in C++ - Stack Overflow.
Summarizing, the local variable i
is of type int
and thus can contain negative values. The remainder operator (%
) has implementation-defined behavior (until C++20). The gnu compiler returns the remainder (rounding towards zero). Thus, i % 6
can return negative values, none of which are handled in the switch statement.
See sample code online.
I first looked for a minimal fix:
- Make
i
an unsigned integer? - Perform the remainder operation twice? [[
(((i % 6) + 6) % 6)
]] - Handle the cases of
i % 6
being in{ -5, -4, -3, -2, -1 }
? - Both unsigned integer and remainder twice?
Unfortunately, none of the above resolved the GCC warning that this could be using uninitialized variables. So, what’s the right fix?