Signed/unsigned comparisons

C++Visual Studio-2005ComparisonUnsignedSigned

C++ Problem Overview


I'm trying to understand why the following code doesn't issue a warning at the indicated place.

//from limits.h
#define UINT_MAX 0xffffffff /* maximum unsigned int value */
#define INT_MAX  2147483647 /* maximum (signed) int value */
            /* = 0x7fffffff */

int a = INT_MAX;
//_int64 a = INT_MAX; // makes all warnings go away
unsigned int b = UINT_MAX;
bool c = false;

if(a < b) // warning C4018: '<' : signed/unsigned mismatch
    c = true;
if(a > b) // warning C4018: '<' : signed/unsigned mismatch
    c = true;
if(a <= b) // warning C4018: '<' : signed/unsigned mismatch
    c = true;
if(a >= b) // warning C4018: '<' : signed/unsigned mismatch
    c = true;
if(a == b) // no warning <--- warning expected here
    c = true;
if(((unsigned int)a) == b) // no warning (as expected)
    c = true;
if(a == ((int)b)) // no warning (as expected)
    c = true;

I thought it was to do with background promotion, but the last two seem to say otherwise.

To my mind, the first == comparison is just as much a signed/unsigned mismatch as the others?

C++ Solutions


Solution 1 - C++

When comparing signed with unsigned, the compiler converts the signed value to unsigned. For equality, this doesn't matter, -1 == (unsigned) -1. For other comparisons it matters, e.g. the following is true: -1 > 2U.

EDIT: References:

5/9: (Expressions)

> Many binary operators that expect > operands of arithmetic or enumeration > type cause conversions and yield > result types in a similar way. The > purpose is to yield a common type, > which is also the type of the result. > This pattern is called the usual > arithmetic conversions, which are > defined as follows:

> * If either > operand is of type long double, the > other shall be converted to long > double.

>* Otherwise, if either operand > is double, the other shall be > converted to double.

>* Otherwise, if > either operand is float, the other > shall be converted to float.

> * Otherwise, the integral promotions > (4.5) shall be performed on both > operands.54)

> * Then, if either operand > is unsigned long the other shall be > converted to unsigned long.

> * Otherwise, if one operand is a long > int and the other unsigned int, then > if a long int can represent all the > values of an unsigned int, the > unsigned int shall be converted to a > long int; otherwise both operands > shall be converted to unsigned long > int.

> * Otherwise, if either operand is > long, the other shall be converted to > long.

>* Otherwise, if either operand > is unsigned, the other shall be > converted to unsigned.

4.7/2: (Integral conversions)

> If the destination type is unsigned, > the resulting value is the least > unsigned integer congruent to the > source integer (modulo 2n where n is > the number of bits used to represent > the unsigned type). [Note: In a two’s > complement representation, this > conversion is conceptual and there is > no change in the bit pattern (if there > is no truncation). ]

EDIT2: MSVC warning levels

What is warned about on the different warning levels of MSVC is, of course, choices made by the developers. As I see it, their choices in relation to signed/unsigned equality vs greater/less comparisons make sense, this is entirely subjective of course:

-1 == -1 means the same as -1 == (unsigned) -1 - I find that an intuitive result.

-1 < 2 does not mean the same as -1 < (unsigned) 2 - This is less intuitive at first glance, and IMO deserves an "earlier" warning.

Solution 2 - C++

Why signed/unsigned warnings are important and programmers must pay heed to them, is demonstrated by the following example.

Guess the output of this code?

#include <iostream>
 
int main() {
        int i = -1;
        unsigned int j = 1;
        if ( i < j ) 
            std::cout << " i is less than j";
        else
            std::cout << " i is greater than j";
           
        return 0;
}

Output:

i is greater than j

Surprised? Online Demo : http://www.ideone.com/5iCxY

Bottomline: in comparison, if one operand is unsigned, then the other operand is implicitly converted into unsigned if its type is signed!

Solution 3 - C++

The == operator just does a bitwise comparison (by simple division to see if it is 0).

The smaller/bigger than comparisons rely much more on the sign of the number.

4 bit Example:

1111 = 15 ? or -1 ?

so if you have 1111 < 0001 ... it's ambiguous...

but if you have 1111 == 1111 ... It's the same thing although you didn't mean it to be.

Solution 4 - C++

In a system that represents the values using 2-complement (most modern processors) they are equal even in their binary form. This may be why compiler doesn't complain about a == b.

And to me it's strange compiler doesn't warn you on a == ((int)b). I think it should give you an integer truncation warning or something.

Solution 5 - C++

The line of code in question does not generate a C4018 warning because Microsoft have used a different warning number (i.e. C4389) to handle that case, and C4389 is not enabled by default (i.e. at level 3).

From the Microsoft docs for C4389:

// C4389.cpp
// compile with: /W4
#pragma warning(default: 4389)

int main()
{
   int a = 9;
   unsigned int b = 10;
   if (a == b)   // C4389
      return 0;
   else
      return 0;
};

The other answers have explained quite well why Microsoft might have decided to make a special case out of the equality operator, but I find those answers are not super helpful without mentioning C4389 or how to enable it in Visual Studio.

I should also mention that if you are going to enable C4389, you might also consider enabling C4388. Unfortunately there is no official documentation for C4388 but it seems to pop up in expressions like the following:

int a = 9;
unsigned int b = 10;
bool equal = (a == b); // C4388

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionPeterView Question on Stackoverflow
Solution 1 - C++ErikView Answer on Stackoverflow
Solution 2 - C++NawazView Answer on Stackoverflow
Solution 3 - C++Yochai TimmerView Answer on Stackoverflow
Solution 4 - C++HosseinView Answer on Stackoverflow
Solution 5 - C++Tim RaeView Answer on Stackoverflow