61

See this code snippet

int main()
{ 
 unsigned int a = 1000;
 int b = -1;
 if (a>b) printf("A is BIG! %d\n", a-b);
 else printf("a is SMALL! %d\n", a-b); 
 return 0;
}   

This gives the output: a is SMALL: 1001

I don't understand what's happening here. How does the > operator work here? Why is "a" smaller than "b"? If it is indeed smaller, why do i get a positive number (1001) as the difference?

R Sahu
  • 204,454
  • 14
  • 159
  • 270
Gitmo
  • 2,366
  • 5
  • 25
  • 31
  • 2
    If you use the -Wsign-compare compiler flag you will get a warning for the comparison. You should always use -Wall (which includes -Wsign-compare). See [here](http://www.a-coding.com/2010/12/beware-usigned-integers.html) for other ways to avoid this problem. – Alejandro Dec 05 '10 at 20:58
  • See this post for more info: http://stackoverflow.com/q/10474769/844882 – Adrian Monk May 07 '12 at 01:44
  • 1
    @Aleph7 - technically that isn't quite right, -Wsign-compare is only inc. with -Wall *if* you are compiling C++. It isn't included for C (see here https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html) I've tested it and can confirm the code above gives no warning with -Wall but does with -Wsign-compare (I'm using gcc (Ubuntu 5.2.1-22ubuntu2) 5.2.1 20151010) – bph Nov 10 '16 at 20:09

7 Answers7

65

Binary operations between different integral types are performed within a "common" type defined by so called usual arithmetic conversions (see the language specification, 6.3.1.8). In your case the "common" type is unsigned int. This means that int operand (your b) will get converted to unsigned int before the comparison, as well as for the purpose of performing subtraction.

When -1 is converted to unsigned int the result is the maximal possible unsigned int value (same as UINT_MAX). Needless to say, it is going to be greater than your unsigned 1000 value, meaning that a > b is indeed false and a is indeed small compared to (unsigned) b. The if in your code should resolve to else branch, which is what you observed in your experiment.

The same conversion rules apply to subtraction. Your a-b is really interpreted as a - (unsigned) b and the result has type unsigned int. Such value cannot be printed with %d format specifier, since %d only works with signed values. Your attempt to print it with %d results in undefined behavior, so the value that you see printed (even though it has a logical deterministic explanation in practice) is completely meaningless from the point of view of C language.

Edit: Actually, I could be wrong about the undefined behavior part. According to C language specification, the common part of the range of the corresponding signed and unsigned integer type shall have identical representation (implying, according to the footnote 31, "interchangeability as arguments to functions"). So, the result of a - b expression is unsigned 1001 as described above, and unless I'm missing something, it is legal to print this specific unsigned value with %d specifier, since it falls within the positive range of int. Printing (unsigned) INT_MAX + 1 with %d would be undefined, but 1001u is fine.

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
  • Although we know or can guess enough about the calling convention of his implementation to conclude that what has happened is the unsigned result of a-b, which is in fact 1001, has been passed through the varargs unscathed, and reinterpreted as signed without changing the value. – Steve Jessop Jan 18 '10 at 10:49
  • 1
    Yeah passing unsigned int and doing `va_arg(ap, int)` alone is not UB yet. But it's indeed UB to violate printf's requirements on expecting an `int`. It sounds silly to me, though. Why haven't they specified for printf: "The type of the next argument shall be a signed or unsigned int, and shall be within range of int". – Johannes Schaub - litb Jan 18 '10 at 11:13
  • @Johannes: Actually, it might be already specified. See my edit. – AnT stands with Russia Jan 18 '10 at 15:47
  • But whether it has compatible representation is not important, i think. If the Standard says something is UB, then it is UB: It's not superceeded by some statement in a footnote. I think this just means that there is some range of freedome you can do, without hard requirements on part of the standard. Like with reading from different union members (this is not UB up front in C, i think, but can be well defined if both members have compatible representations). But calling some function through a prototype-less function pointer expression with incompatible arguments stays UB, for instance. – Johannes Schaub - litb Jan 18 '10 at 17:49
  • 1
    In this case, `fprintf` description states: (for %d): "The int argument is converted to ..." and "If any argument is not the correct type for the corresponding conversion specification, the behavior is undefined.". So i don't believe it's well defined. Maybe someone from usenet knows? – Johannes Schaub - litb Jan 18 '10 at 17:51
  • I can't figure it out from 6.2.6.2, but I think the standard only compares the value bits of corresponding signed and unsigned integral types, not any padding bits. Is it legal for an implementation to ignore the padding bits in unsigned int, but for any padding bit set in int to be a trap representation? I don't know, but if so then the 1001 generated might just happen to have a padding bit set, and therefore reinterpreting it as int would be U.B. Not to say necessarily that there even are any gcc targets with padding bits in int, let alone ones with this odd property... – Steve Jessop Jan 19 '10 at 01:06
  • What's the rule in the following case: `x = 10 +- 10u + 10u +- 10;`? Being x signed or unsigned gives the same result! – mike Nov 11 '15 at 16:58
16

On a typical implementation where int is 32-bit, -1 when converted to an unsigned int is 4,294,967,295 which is indeed ≥ 1000.

Even if you treat the subtraction in an unsigned world, 1000 - (4,294,967,295) = -4,294,966,295 = 1,001 which is what you get.

That's why gcc will spit a warning when you compare unsigned with signed. (If you don't see a warning, pass the -Wsign-compare flag.)

kennytm
  • 510,854
  • 105
  • 1,084
  • 1,005
  • 2
    I downvoted because of "4,294,967,295 (2's complement)". It has nothing to do with 2's complement. It will yield the same value on a 1's complement machine. And will yield a different value on a different bitwidth integer. – Johannes Schaub - litb Jan 18 '10 at 09:58
  • @Schaub: Maybe I'm not clear but what I mean is 4,294,967,295 (which is 2's complement of 1) is indeed ≥1. Also, the the 1's complement machine the representation of -1 is 4,294,967,294. – kennytm Jan 18 '10 at 10:09
  • 5
    as litb says, it has nothing to do with the representation. On a 1s' complement machine, converting -1 to unsigned results in UINT_MAX, it doesn't result in the 1s' complement bit pattern being reinterpreted. One of the several ways in which 2's complement is convenient, is that C (un)signed conversions don't change the bit pattern. That's particular to 2's complement: the C conversions to unsigned types are defined in terms of modulo arithmetic, not in terms of bit pattern. On 1s' complement, the implementation has to do some actual work to come up with UINT_MAX. – Steve Jessop Jan 18 '10 at 10:56
  • the edit is better, but still there is no guarantee that `UINT_MAX` is 4,294,967,295. Also see http://stackoverflow.com/questions/1863153 – Alok Singhal Jan 18 '10 at 16:15
1

You are doing unsigned comparison, i.e. comparing 1000 to 2^32 - 1.

The output is signed because of %d in printf.

N.B. sometimes the behavior when you mix signed and unsigned operands is compiler-specific. I think it's best to avoid them and do casts when in doubt.

Antti Huima
  • 25,136
  • 3
  • 52
  • 71
  • 1
    The subtraction doesn't make anything signed. Subtraction is the same operation for both signed and unsigned values. – wj32 Jan 18 '10 at 09:35
  • 1
    Incorrect, subtraction between `int` and `unsigned int` operands is evaluated as unsigned subtraction and the result is, of course, unsigned. – AnT stands with Russia Jan 18 '10 at 09:41
1
 #include<stdio.h>
 int main()
 {
   int a = 1000;
   signed int b = -1, c = -2;
   printf("%d",(unsigned int)b);
   printf("%d\n",(unsigned int)c);
   printf("%d\n",(unsigned int)a);

   if(1000>-1){
      printf("\ntrue");
   }
   else 
     printf("\nfalse");
     return 0;
 }

For this you need to understand the precedence of operators

  1. Relational Operators works left to right ... so when it comes

    if(1000>-1)

then first of all it will change -1 to unsigned integer because int is by default treated as unsigned number and it range it greater than the signed number

-1 will change into the unsigned number ,it changes into a very big number

harsh
  • 337
  • 1
  • 2
  • 14
0

The hardware is designed to compare signed to signed and unsigned to unsigned.

If you want the arithmetic result, convert the unsigned value to a larger signed type first. Otherwise the compiler wil assume that the comparison is really between unsigned values.

And -1 is represented as 1111..1111, so it a very big quantity ... The biggest ... When interpreted as unsigned.

DigitalRoss
  • 143,651
  • 25
  • 248
  • 329
  • Hardware may have no arithmetic comparison functionality (as a dedicated compare or subtract instruction) at all, and yet such hardware may still be used to run C programs. Hardware is important but beyond the scope of the programming language as defined by the language standard. – Alexey Frunze Apr 20 '12 at 07:44
0

Find a easy way to compare, maybe useful when you can not get rid of unsigned declaration, (for example, [NSArray count]), just force the "unsigned int" to an "int".

Please correct me if I am wrong.

if (((int)a)>b) {
    ....
}
chenyi1976
  • 1,104
  • 9
  • 17
-1

while comparing a>b where a is unsigned int type and b is int type, b is type casted to unsigned int so, signed int value -1 is converted into MAX value of unsigned**(range: 0 to (2^32)-1 )** Thus, a>b i.e., (1000>4294967296) becomes false. Hence else loop printf("a is SMALL! %d\n", a-b); executed.

Naveen Kumar
  • 63
  • 1
  • 6