I'm getting a clang-tidy warning that reads narrowing conversion from 'int' to 'float' when I convert from a uint8_t to a float, which to my understanding is not a narrowing conversion since float can represent every integer that a uint8_t can.
Code example:
uint8_t testVar = 8;
float test2 = 2.0f * testVar;
clang-tidy flags the second line of that with the warning cppcoreguidelines-narrowing-conversions: narrowing conversion from 'int' to 'float'. In my IDE, the squiggle shows up under the testVar.
According to the reference, this warning should be flagged if we convert from an integer to a narrower floating-point (e.g. uint64_t to float), but to the best of my knowledge, float is not narrower than uint8_t.
Am I fundamentally misunderstanding these data types, or is something else going on here?
I'm on LLVM version 11.0.0 if that matters.
Related
I recently discovered a hard to find bug in a project I am working on. The problem was that we did a calculation that had a uint32_t result and stored that in a uint64_t variable. We expected the result to be a uint64_t because we knew that the result can be too big for a 32 bit unsigned integer.
My question is: is there a way to make the compiler (or a static analysis tool like clang-tidy) warn me when something like this happens?
An example:
#include <iostream>
constexpr uint64_t MUL64 { 0x00000000ffffffff };
constexpr uint32_t MUL32 { 0xffffffff };
int main() {
const uint32_t value { 0xabababab };
const uint64_t value1 { MUL64 * value }; // the result is a uint64_t because
// MUL64 is a uint64_t
const uint64_t value2 { MUL32 * value }; // i'd like to have a warning here
if (value1 == value2) {
std::cout << "Looks good!\n";
return EXIT_SUCCESS;
}
std::cout << "Whoopsie\n";
return EXIT_FAILURE;
}
Edit:
The overflow was expected, i.e. we knew that we would need an uint64_t to store the calculated value. We also know how to fix the problem and we changed it later to something like:
const uint64_t value2 { static_cast<uint64_t>(MUL32) * value };
That way the upper 32 bits aren't cut off during the calculation. But things like that may still happen from time to time, and I just want to know whether there is way to detect this kind of mistakes.
Thanks in advance!
Greetings,
Sebastian
The multiplication behavior for unsigned integral types is well-defined to wrap around modulo 2 to the power of the width of the integer type. Therefore there isn't anything here that the compiler could be warning about. The behavior is expected and may be intentional. Warning about it would give too many false positives.
Also, in general the compiler cannot test for overflow at compile-time outside a constant expression evaluation. In this specific case the values are obvious enough that it could do that though.
Warning about any widening conversion after arithmetic would very likely also be very noisy.
I am not aware of any compiler flag that would add warnings for the reasons given above.
Clang-tidy does have a check named bugprone-implicit-widening-of-multiplication-result specifically for this case of performing a multiplication in a narrower type which is then implicitly widened. It seems the check is present since LLVM 13. I don't think there is an equivalent for addition though.
This check works here as expected:
<source>:11:29: warning: performing an implicit widening conversion to type 'const uint64_t' (aka 'const unsigned long') of a multiplication performed in type 'unsigned int' [bugprone-implicit-widening-of-multiplication-result]
const uint64_t value2 { MUL32 * value }; // i'd like to have a warning here
^
<source>:11:29: note: make conversion explicit to silence this warning
const uint64_t value2 { MUL32 * value }; // i'd like to have a warning here
^~~~~~~~~~~~~
static_cast<const uint64_t>( )
<source>:11:29: note: perform multiplication in a wider type
const uint64_t value2 { MUL32 * value }; // i'd like to have a warning here
^~~~~
static_cast<const uint64_t>()
Clang's undefined behavior sanitizer also has a check that flags all unsigned integer overflows at runtime, which is not normally included in -fsanitize=undefined. It can be included with -fsanitize=unsigned-integer-overflow. That will very likely require adding suppressions for intended wrap-around behavior. See https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html for details.
It seems however that this check isn't applied here since the arithmetic is performed by the compiler at compile-time. If you remove the const on value2, UBSan does catch it:
/app/example.cpp:11:29: runtime error: unsigned integer overflow: 4294967295 * 2880154539 cannot be represented in type 'unsigned int'
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior /app/example.cpp:11:29 in
Whoopsie
GCC does not seem to have an equivalent option.
If you want consistent warnings for overflow in unsigned arithmetic, you need to define your own wrapper classes around the integer types that perform the overflow check and e.g. throw an exception if it fails or alternatively you can implement functions for overflow-safe addition/multiplication which you would then have to use instead of the + and * operators.
Does anybody know if there is a way to disable this kind of warning given by clang in CMAKE please ?
std::vector<float> v{1.f, 2.f, 1.f};
int i = 1;
float foo = v[i]; // Here we get the warning, variable i should be a size_t
...
Implicit conversion changes signedness: 'int' to 'std::__1::vector<float>::size_type' (aka 'unsigned long')
Clang compiler gives me a warning on v[i] as it wants me to cast it as a size_t which is quite verbose if I do explicit casting everywhere.
Also, I don't want to use size_t. For me this type it is error prone as it breaks the standard substraction when getting negative results. (my vectors will never exceed 2^31 btw)
I was analyzing some warnings in a codebase and got puzzled by this one generated by Clang
Consider the following C++ code:
#include <iostream>
int main(int , char *[])
{
uint32_t val1 = 10;
int32_t val2 = -20;
int32_t result = val1 + val2;
std::cout << "Result is " << result << "\n";
return 0;
}
Clang gives me the following warning when compiling this code with -Wconversion
<source>:9:25: warning: implicit conversion changes signedness:
'unsigned int' to 'int32_t' (aka 'int') [-Wsign-conversion]
int32_t result = val1 + val2;
~~~~~~ ~~~~~^~~~~~
<source>:9:27: warning: implicit conversion changes signedness:
'int32_t' (aka 'int') to 'unsigned int' [-Wsign-conversion]
int32_t result = val1 + val2;
~ ^~~~
2 warnings generated.
GCC also gives me this warning, however I need to provide -Wsign-conversion to trigger it.
The warning says that val2 will be cast to an unsigned int and therefore will loose it's sign. So far so good. However I was expecting that the code above would produce incorrect output, to my surprise it works perfectly fine.
Result is -10
See the program running on both compilers on godbolt.
The cast does not happen in the compiled code and val2 keep its original value. The result of the computation is correct. What is the actual danger that this warning is warning me against? How can I trigger this behaviour? Is the warning bogus?
The second conversion is where things become implementation dependent.
The first (evaluation of expression val1+val2) triggers conversion of val2 to unsigned from signed, which is standard-compliant and documented. The result of the expression is therefore unsigned.
The second (conversion of the resulting unsigned back to signed) is where potential problems ensue. If the unsigned value is not within the defined domain of the target signed type (and in this case, it isn't), implementation behavior ensues, which you cannot assume is portable across the known universe.
What is the actual danger that this warning is warning me against?
The potential danger is that you may have been unaware of the implicit sign conversion, and have made it by accident. If it is intentional and behaves as desired, then there is no danger).
How can I trigger this behaviour?
You already have triggered an implicit sign conversion.
That said, if you want to see some output which may be surprising to you, try this instead:
std::cout << val1 + val2;
Is the warning bogus?
Depends on your definition of bogus.
There definitely is an implicit sign conversion in the program, and therefore if you ask the compiler to warn about implicit sign conversions, then it is entirely correct for the compiler to warn about the implicit sign conversion that is in the program.
There is a reason why this warning option is not enabled by default, nor when enabling "all" warnings using -Wall, nor even when enabling "extra" warnings with -Wextra. These sign conversion warnings warn about a program with well defined behaviour, but which may be surprising to someone who is not paying close attention. A program can be meaningful and correct despite this warning.
static int var[2] __attribute__ ((aligned (8))) =
{
0x0255cfa8,
0xfdfcddfc
};
Why am I getting a warning: narrowing conversion of '4261207548u' from 'unsigned int' to 'int' inside { } is ill-formed in C++11 [-Wnarrowing]?
Even though the numbers have no u or U suffix they seem to be taken as unsigned?
If int is 32 bit on your platform, then 0xfdfcddfc is an unsigned. That's because you've used hexadecimal notation.
Your helpful compiler is warning you that that number is too big for an int.
Note that if you written the denary equivalent, then it would be a signed type (long or long long) and the compiler would have issued a subtly different warning.
Reference: https://en.cppreference.com/w/cpp/language/integer_literal#The_type_of_the_literal
i have this warning. Can u help me ?
warning: narrowing conversion of ‘action’ from ‘uint32 {aka unsigned int}’ to ‘uint8 {aka unsigned char}’
How to convert uint32 to uint8 securely ?
Thanks
The warning is correct, such a conversion is potentially dangerous (it loses 24 higher bits).
You can tell the compiler that you really want this [unsafe] conversion by using explicit cast:
uint8_t my_byte_var = static_cast<uint8_t>(my_int32_var);
This should suppress the warning.
The narrowing warning/error should only arise if you use list-initialization, e.g.:
uint32_t action = 12345;
uint8_t val{action};
You could instead just use normal assignment:
uint8_t val = action;
The narrowing is still going to happen though, as there is no way for 12345 to fit in a uint8_t, so you'll be left with 9. If you're OK with that, then there's your solution. If not, then it's a good thing there's a warning because you're using the wrong type!