Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Explain integer comparison with promotion

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?

like image 588
Ralf Avatar asked Jun 27 '19 18:06

Ralf


People also ask

What is integer promotion?

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.

Is short promoted to 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.

What is operator promotion in C?

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.

What is type of promotion 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.

What is integer promotion in C?

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.

What happens to the values after implicit type promotion?

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.

What is the difference between an integer and an integer?

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.

How do you know if an integer is equal to another?

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.


2 Answers

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:

  1. to alert you to code that might behave differently on other systems.
  2. to alert you to code that might not behave as the coder intended.

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.

like image 142
M.M Avatar answered Oct 28 '22 02:10

M.M


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.

like image 26
Oblivion Avatar answered Oct 28 '22 04:10

Oblivion