views:

578

answers:

8
int a = -534;
unsigned int b = (unsigned int)a;
printf("%d, %d", a, b);

prints -534, -534

Why is the typecast not taking place?

I expected it to be -534, 534


If I modify the code to

int a = -534;
unsigned int b = (unsigned int)a;
if(a < b)
  printf("%d, %d", a, b);

its not printing anything... after all a is less than b??

+14  A: 

Because you use %d for printing. Use %u for unsigned. Since printf is a vararg function, to cannot know the types of the parameters and must instead rely on the format specifiers. Because of this the type cast you do has no effect.

Tronic
In fact, the unsigned value gets `reinterpret_cast`-ed _back_ by `printf()`, because of `%d`. `%d` means "take the value and interpret it as signed int"
Vlad
Yep. According to the C Standard, undefined behavior results (not in general for variadic function, but for printf in particular).
Johannes Schaub - litb
Why does the `if` fail? Please see the edited ques.
Lazer
Because an operation involving an unsigned argument gets performed with unsigned arithmetics. Thus it implicitly casts a to unsigned before comparison.
Tronic
Converting a negative value to an `unsigned int` value doesn't negate it (changing -534 to 534), it converts it to the smallest positive number that is congruent to the original modulo `UINT_MAX + 1`. If you have 32 bit `unsigned int`s, -534 will convert to 4294966762 (on a 2s complement machine, this has the same bitwise representation as -534).
caf
@Vlad - They're not `reinterpret_cast`-ed. `printf()` is a C function. They're just casted.
Chris Lutz
@Chris: I didn't mean an operation of `reinterpret_cast`, I just meant the semantics: the binary representation is interpreted as int without any regard which was the type of the argument. In C++ (which we all are familiar with) this semantics is expressed by `reinterpret_cast`, whereas in C the cast is a mixture of different semantics (at least from C++ point of view).
Vlad
+4  A: 

your specifier in the printf is asking printf to print a signed integer, so the underlying bytes are interpreted as a signed integer.

You should specify that you want an unsigned integer by using %u.

edit: a==b is true for the comparison, which is odd behaviour, but it's perfectly valid. You haven't changed the underlying bits you have only asked the compiler to treat the underlying bits in a certain way. Therefore a bitwise comparison yields true.

[speculation] I would suspect that behaviour might vary among compiler implementations -i.e., a fictitious CPU might not use the same logic for both signed and unsigned numerals in which case a bitwise comparison would fail. [/speculation]

Hassan Syed
I don't think you wrote what you meant to write :)
static_rtti
Yep that is correct, plus I'm dyslexic hehe.
Hassan Syed
Why does the `if` fail? Please see the edited question.
Lazer
As stated, because a is equal to b, it is not less than. The underlying data is the same.You should watch out for compiler warnings to spot things like these (C4018 is one the above code would trigger)http://msdn.microsoft.com/en-us/library/y92ktdf2%28VS.80%29.aspx
Danny Parker
As stated in another comment by someone else, this behavior of the comparison will be the same across all C compiler/combinations.
Johannes Schaub - litb
Hassan, the equality is not due to the underlying bits, but because the C standard guarantees so. Even in the case of sign-magnitude encoding for example, where `a` and `b` have different bits, `a == b` is true. See my answer for details.
Alok
A: 

I guess the first case of why b is being printed as -534 has been sufficiently answered by Tronic and Hassan. You should not be using %d and should be using %u.

As far as your second case is concered, again an implicit typecasting will be happening and both a and b will be same due to which your comparison does yield the expected result.

Jay
A: 

As far as I can see, the if fails because the compiler assumes the second variable should be considered the same type as the first. Try if(b > a) to see the difference.

Arthur Kalliokoski
This answer is just wrong: in a test comparing a signed int with an unsigned int, the signed int is cast to an unsigned int. Both (a < b) and (a > b) are false because (a == b).
Stephen C. Steel
Both (a < b) and (a > b) are falseBut neither one is if(b > a). My compiler agrees.
Arthur Kalliokoski
A: 

Re 2nd question: comparison never works between two different types - they are always implicitly cast to the "lowest common denominator", which in this case will be unsigned int. Nasty and counter-intuitive, I know.

SF.
If this is the case shouldn't the cast take -534 to zero, or cause a run-time fault ? I compiled the code in VS2008 and `a==b` is true.
Hassan Syed
No - the cast of -534 to `unsigned int` is allowed in both cases (case #1 when initializing `b`, and case #2 when comparing `a` and `b`).
MSalters
+1  A: 

C can be an ugly beast sometimes. The problem is that -534 always represents the value 0xfffffdea whether it is stored in a variable with the type unsigned int or signed int. To compare these variables they must be the same type so one will get automatically converted to either an unsigned or signed int to match the other. Once they are the same type they are equal as they represent the same value.

It seems likely that the behaviour you want is provided by the function abs:

int a = -534;
int b = abs(a);
printf("%d, %d", a, b);
Russ Hayward
Note that the number of f's in `0xfffffdea` may vary. E.g. on 16 bits machines it will be just `0xfdea` and on 64 bits machines you'll have 13 f's followed by `dea`.
MSalters
Absolutely right. I thought when I wrote the answer that I should mention something about int size but I thought it might clutter the answer with information that was not particularly relevant to the problem at hand.
Russ Hayward
More importantly, even the `dea` part is not necessarily correct, on a sign-magnitude or ones' complement machine.
Alok
A: 

Casting an integer type from signed to unsigned does not modify the bit pattern, it merely changes the interpretation of the bit pattern.

You also have a format specifier mismatch, %u should be used for unsigned integers, but even then the result will not be 534 as you expect, but 4294966762.

If you want to make a negative value positive, simply negate it:

unsigned b = (unsigned)-a ;
printf("%d, %u", a, b);

As for the second example, operations between types with differing signed-ness involve arcane implicit conversion rules - avoid. You should set your compiler's warning level high to trap many of these errors. I suggest /W4 /WX in VC++ and -Wall -Werror -Wformat for GCC for example.

Clifford
+1  A: 

First, you don't need the cast: the value of a is implicitly converted to unsigned int with the assignment to b. So your statement is equivalent to:

unsigned int b = a;

Now, an important property of unsigned integral types in C and C++ is that their values are always in the range [0, max], where max for unsigned int is UINT_MAX (it's defined in limits.h). If you assign a value that's not in that range, it is converted to that range. So, if the value is negative, you add UINT_MAX+1 repeatedly to make it in the range [0, UINT_MAX]. For your code above, it is as if we wrote: unsigned int b = (UINT_MAX + a) + 1. This is not equal to -a (534).

Note that the above is true whether the underlying representation is in two's complement, ones' complement, or sign-magnitude (or any other exotic encoding). One can see that with something like:

signed char c = -1;
unsigned int u = c;
printf("%u\n", u);
assert(u == UINT_MAX);

On a typical two's complement machine with a 4-byte int, c is 0xff, and u is 0xffffffff. The compiler has to make sure that when value -1 is assigned to u, it is converted to a value equal to UINT_MAX.

Now going back to your code, the printf format string is wrong for b. You should use %u. When you do, you will find that it prints the value of UINT_MAX - 534 + 1 instead of 534.

When used in the comparison operator <, since b is unsigned int, a is also converted to unsigned int. This, given with b = a; earlier, means that a < b is false: a as an unsigned int is equal to b.

Let's say you have a ones' complement machine, and you do:

signed char c = -1;
unsigned char uc = c;

Let's say a char (signed or unsigned) is 8-bits on that machine. Then c and uc will store the following values and bit-patterns:

+----+------+-----------+
| c  |  -1  | 11111110  |
+----+------+-----------+
| uc | 255  | 11111111  |
+----+------+-----------+

Note that the bit patterns of c and uc are not the same. The compiler must make sure that c has the value -1, and uc has the value UCHAR_MAX, which is 255 on this machine.

There are more details on my answer to a question here on SO.

Alok