I'm trying to understand how integer promotion and comparison in and 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?
Integer promotion is the implicit conversion of a value of any integer type with rank less or equal to rank of int or of a bit field of type _Bool, int, signed int, unsigned int, to the value of type int or unsigned int.
Some data types like char , short int take less number of bytes than int, these data types are automatically promoted to int or unsigned int when an operation is performed on them.
Type Promotion in C. Introduction. Type promotion in C is a method to convert any variable from one datatype to another. C allows variables of different datatypes to be present in a single expression. There are different types of type conversions available in C.
C++ promotions are "value-preserving," as the value after the promotion is guaranteed to be the same as the value before the promotion.
Integer Promotions in C. Some data types like char , short int take less number of bytes than int, these data types are automatically promoted to int or unsigned int when an operation is performed on them. This is called integer promotion. For example no arithmetic calculation happens on smaller types like char, short and enum.
After implicit type promotion, the values are compared and found unequal. For more clarity, refer to Figure 5. Let us dig a little deeper by obtaining the assembly code for Code-1 and analysing the disassembled code as shown in Figure 6, the explanation for which is given in Table 1.
An integer is less than another integer if it is located to the left of the other integer on the number line. An integer is greater than another integer if it is located to the right of the other integer on the number line. An integer is equal to another integer if it is exactly the same. To unlock this lesson you must be a Study.com Member.
An integer is less than another integer if it is located to the left of the other integer on the number line. An integer is greater than another integer if it is located to the right of the other integer on the number line. An integer is equal to another integer if it is exactly the same.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With