Reputation: 151
I'm trying to understand how integer promotion and comparison in a c++ application works.
#include <cstdint>
int main(void)
{
uint32_t foo = 20;
uint8_t a = 2;
uint8_t b = 1;
uint8_t c = 5;
if(foo == b*c) {}
if(foo == a) {}
if(foo == a + c) {}
if(foo == a + b*c) {}
return 0;
}
Only for the last comparison i get a compiler warning: "comparison between signed and unsigned integer expressions [-Wsign-compare]".
Why does this only happen in the last case but not in the others?
Upvotes: 15
Views: 1828
Reputation: 7374
since the type of operands are different a set of implicit conversions take place to reach a common type.
For the binary operators (except shifts), if the promoted operands have different types, additional set of implicit conversions is applied, known as usual arithmetic conversions with the goal to produce the common type (also accessible via the std::common_type type trait)
because of integral types here integral conversions is applied to:
- If either operand has scoped enumeration type, no conversion is performed: the other operand and the return type must have the same
type
- Otherwise, if either operand is long double, the other operand is converted to long double
- Otherwise, if either operand is double, the other operand is converted to double
- Otherwise, if either operand is float, the other operand is converted to float
- Otherwise, the operand has integer type (because bool, char, char8_t, char16_t, char32_t, wchar_t, and unscoped enumeration were promoted at this point) and integral conversions are applied to produce the common type, as follows:
- If both operands are signed or both are unsigned, the operand with lesser conversion rank is converted to the operand with the greater integer conversion rank
- Otherwise, if the unsigned operand's conversion rank is greater or equal to the conversion rank of the signed operand, the signed operand is converted to the unsigned
operand's type.- Otherwise, if the signed operand's type can represent all values of the unsigned operand, the unsigned operand is converted to the signed operand's type Otherwise, both operands are converted to the unsigned counterpart of the signed operand's type.
The same arithmetic conversions apply to comparison operators too.
from all this one can conclude since the rhs
are all uint8_t
the common type will be int, and then since the rhs
is uint32_t
the common type of ==
operator will be uint32_t
.
but for some reason that I have no idea gcc
don't do the last conversion while clang does it. see the gcc
type conversion for +
operator in godblot
It also could happen that the warning is a false warning and the conversion took place, as it happened for +
operator.
See how clang
sees the last if
(cppinsights):
if(foo == static_cast<unsigned int>(static_cast<int>(a) + (static_cast<int>
(b) * static_cast<int>(c))))
Update:
I couldn't find a difference in the assembly generated by the two compilers and would agree with @M.M so, IMO it's a gcc
bug.
Upvotes: 5
Reputation: 141554
It's a compiler "bug". To elaborate on this:
In general, comparison between signed and unsigned relies on implementation-defined quantities (the sizes/ranges of types). For example USHRT_MAX == -1
is true on common 16-bit systems, and false on common 32-bit systems. The answer by "oblivion" goes into more technical detail about this.
All of your code examples are well-defined and behave the same on all (conforming) systems.
The intent of this warning is twofold:
However, in general. it's not such a simple job for the compiler's static analysis to sort out the first case, let alone the second case which is rather subjective.
IMO the warning, for your code, is a bug because the code is well-defined and there is nothing to warn about.
Personally I don't enable this warning: I'm familiar with the rules for signed-unsigned comparison and prefer to avoid mangling my code to suppress the warning.
Going to the opposite extreme, some people prefer to avoid all signed-unsigned comparisons in their code even when it is well-defined; and they would consider it a bug that the compiler doesn't warn about your first three code examples.
GCC has tended to err on the side of warning too much, but they are in the situation that they can't please everyone.
Upvotes: 1