Logan
Logan

Reputation: 53112

WHY is comparison of unsigned expression < 0 always false

So, if you ever try evaluating an unsigned integer < 0, you get a warning that says:

comparison of unsigned expression < 0 always false

This is because unsigned integers are never technically negative, here's the best explanation I've found so far: here

But if I run something like this:

for (int i = 0; i < 10; i++) {
    testInt--;
    NSLog(@"TestInt = %li", testInt);
    
    // WARNING AREA //
    if (testInt < 0) {
        NSLog(@"HI");
    }
 
    // OK //
    if (testInt == -1) {
        NSLog(@"Negative ...");
    }
}

I'll get output:

TestInt = 4

TestInt = 3

TestInt = 2

TestInt = 1

TestInt = 0

TestInt = -1

Negative ...

TestInt = -2

TestInt = -3

TestInt = -4

TestInt = -5

You can see that the number evaluates fine for negative values, and can equal -1.

Update

After @RobP's answer, I noticed a problem with my logging, it should be %lu which gives the output:

TestInt = 4

TestInt = 3

TestInt = 2

TestInt = 1

TestInt = 0

TestInt = 18446744073709551615

Negative ...

TestInt = 18446744073709551614

TestInt = 18446744073709551613

TestInt = 18446744073709551612

TestInt = 18446744073709551611

However, my question still stands because it can still evaulate numb == -1 and not numb < 0:

Question

Why can a NSUInteger be simultaneously equal to -1 but not less than 0?

Upvotes: 1

Views: 3265

Answers (3)

Alexandru Barbarosie
Alexandru Barbarosie

Reputation: 2992

Technically your number is never equal to -1. When you compare your unsigned number to a signed -1, the last is converted to an unsigned as well, hence yielding a value that is equal to testInt.

The comparison of an unsigned versus a 0 and mainly using the less operator is still always false, because 0 is an unsigned, hence no promotion from one data type to another doesn't takes place.

Upvotes: 0

M.M
M.M

Reputation: 141554

There's no "technically" about it. Unsigned integers are never negative, full stop. They either hold 0 or a positive value. This doesn't need any further explanation.

They don't "remember" if they were originally assigned from a signed value, or anything like that.

As you discovered, your original code caused undefined behaviour by using the wrong format specifier for printf. All that tells you is (at best) that the bits used to store this unsigned int are the same bits that would be used to store a particular signed int. But the meaning of the bits is different, that's what data types do: they tell how a certain set of bits should be interpreted as a value.

In the code numb == -1 , there is a problem. You are using == on two different data types. In Java for example, the compiler rejects this outright. However, in C and C++ there are some rules called implicit conversions that will guess what you were most likely trying to do, and perform a conversion on one of the operands to match the other one.

In this particular case, the rule is that if one operand has type signed T and the other unsigned T, then the signed one is convered to unsigned. So your code actually behaves like:

numb == (unsigned long)-1

(assuming numb is unsigned long). Further, part of the definition of unsigned long is that when you assign to it an out-of-range value, that value is adjusted modulo ULONG_MAX + 1 until it comes in range. So the code is also equivalent to:

numb == ULONG_MAX

This is why the first output you see after 0 is ULONG_MAX.

Upvotes: 1

RobP
RobP

Reputation: 9522

The problem lies in your display of the numbers, not the numbers. This line:

NSLog(@"TestInt = %li", testInt);

takes your unsigned integer and displays it as if it's a signed int. Change the format specifier to %lu and you should see what your values "really" are to the machine.

EDIT: Thanks to Massa for comment below about the equality of the comparison. When you compare your unsigned variable to the signed value -1, the -1 gets changed into an unsigned value (normally max value of an unsigned int) and they are equal, just as when you take an unsigned int equal to 0 and subtract 1, it becomes UINT_MAX.

Upvotes: 2

Related Questions