Home » C++ » Signed/unsigned comparisons

Signed/unsigned comparisons

Posted by: admin November 29, 2017 Leave a comment

Questions:

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?

Answers:

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.

Questions:
Answers:

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!

Questions:
Answers:

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.

Questions:
Answers:

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.

Leave a Reply

Your email address will not be published. Required fields are marked *