tags:

views:

269

answers:

4

I don't get this one!

#include <stdio.h>

int main()
{
    unsigned short t1 = 0, t2 = 0;

    if( t1 < t2-1 )
        printf(" t1 < t2-1\n");

    unsigned long s1 = 0, s2 = 0;

    if( s1 < s2-1 )
        printf(" s1 < s2-1\n");
}

this results in:

 s1 < s2-1

Either both should fail or both not. I tried this with gcc 4 & 4.2

+13  A: 

Iam not sure but I suspect that the expression t2-1 automatically widened into an int value. I do not have the c standard here what the exact conversion rules are, but I believe types smaller than int are automatically widened.

codymanix
+1, in an arithmetic expression, objects of types (signed/unsigned) short and (signed/unsigned/) char are *promoted* to int.
avakar
This answer would be much better if you said **`t2`** is converted to int, not **`t2-1`** (so that it's clear that the conversion happens *before* the subtraction), though.
Johannes Schaub - litb
+3  A: 

C coercions, as you're discovering, are not always obvious, whenever you operate between different types. t2 is u16, 1 is int (presumably 32-bits), so t2-1 is exactly such an "operation between different types" and results in an overall coercion to int (as it's "longer" than u16...). Later, as s2 and 1 are both 32-bits (though of different signedness), the overall coercion is to unsigned long. So, the sizes of the types involved do help determine the signedness of the overall coercion.

I suggest avoiding mixed-signedness (and ideally also mixed-size!) operations (via casting or special literal notation for literals such as 1 that otherwise will have int type and make your life potentially complicated and your code potentially unportable;-).

Alex Martelli
A: 

-1 is represented as all 1s. Therefore when interpreted as unsigned, its value is 2^32-1, which is clearly greater than 0. I'd guess that the first comparison is getting expanded to perform 32-bit signed arithmetic (perhaps due to the "1" being a signed int).

Note that the following WILL get to the printf, because the comparison is now done in 16-bit unsigned space again:

u32 temp = t2 - 1;
if( t1 < temp )
    printf(" t1 < t2-1\n");
Yuliy
The literal 1 (of type `int`) is converted to `unsigned long` before the subtraction. Since arithmetic on unsigned types is always performed modulo `2^k`, for type-specific `k`, the result is `2^k-1`. The representation of -1 is not involved (and it needn't be all ones on some architectures).
avakar
The representation of -1 is involved in explaining the second part: why 0 < -1 in unsigned longs.
Yuliy
No it isn't. Like avakar says, `-N` -> unsigned is a purely mathematically operation, which just computes `UINT_MAX+1 - N`. No representation is relied on anywhere.
Johannes Schaub - litb
+6  A: 

The C language performs the "Usual arithmetic conversions" for many operators - the conversions are outlined in 6.3.1.8 of the C99 standard. For integral operands, first promotions are performed, and this is what's causing your issue. The promotions are outlined in 6.3.1.1 (Arithmetic operands/Boolean, characters,and integers), which says among other things:

If an int can represent all values of the original type, the value is converted to an int; otherwise, it is converted to an unsigned int. These are called the integer promotions. All other types are unchanged by the integer promotions.

The promotions are applied only to objects or expressions with an integer type with a rank less than int and unsigned int (or bitfields).

So in your exression:

t1 < t2-1

even though the variables are unsigned short they are promoted to int, since on your platform int can represent all the values of unsigned short. So the expression is evaluated using int types, and no underflow occurs - the t2-1 part of the expression ends up as negative 1.

In the expression:

s1 < s2-1

the unsigned long types aren't promoted, because they have a higher 'rank' than int/unsigned int, so the expression is evaluated using unsigned arithmetic (with the underflow from the subtraction), and the s2-1 subexpression evaluates to a very large number, not negative 1.

As litb indicated in a comment, if the platform had int implemented as a 16-bit type (which is permitted - MS-DOS for example), the promotion of unsigned short would be to unsigned int instead of int, since an int wouldn't be able to represent all values of unsigned short (unsigned short must be at least 16-bits). In that case, both if statements would evaluate to true.

Michael Burr