56

In the C programming language, unsigned int is used to store positive values only. However, when I run the following code:

unsigned int x = -12;
printf("%d", x);

The output is still -12. I thought it should have printed out: 12, or am I misunderstanding something?

Eric Leschinski
  • 146,994
  • 96
  • 417
  • 335
ipkiss
  • 13,311
  • 33
  • 88
  • 123
  • Note that because negative numbers are almost always represented with [two's complement](http://en.wikipedia.org/wiki/Two%27s_complement), it actually would be a high value. – Maxpm Mar 02 '11 at 15:32
  • I got two's complement. But I still do not understand why it does not print out 12. more details, please? – ipkiss Mar 02 '11 at 15:35
  • possible duplicate of [Unsigned long with negative value](http://stackoverflow.com/questions/2455806/unsigned-long-with-negative-value) – AProgrammer Mar 02 '11 at 15:36
  • Duplicate: http://stackoverflow.com/questions/2455806/unsigned-long-with-negative-value – AProgrammer Mar 02 '11 at 15:36
  • 44
    You're lying to the compiler: you're telling the compiler you will give `printf` an `int` (the `"%d"`), but instead you give it an `unsigned`. **Don't lie to the compiler** – pmg Mar 02 '11 at 15:37
  • What confuses me more: the compiler doesn't warn about about assigning `-12` to an `unsigned int`, even with all warning enabled (`-Wall -Wextra` with gcc). – Joachim Sauer Mar 02 '11 at 15:44
  • 1
    @Joachim: the compiler is doing the 'usual arithmetic conversions' (see 3.2.1.5). **"... Otherwise, if either operand has type unsigned int, the other operand is converted to unsigned int ..."** and `-12` converted to unsigned results in `(UINT_MAX + 1) - 12`. – pmg Mar 02 '11 at 16:01
  • @pmg: I see. I'm spoiled by Java. I'd expect the compiler to *at least* complain when that step is necessary for a compile-time constant. – Joachim Sauer Mar 02 '11 at 16:04
  • 2
    @Joachim: the gcc flag `-Wsign-conversion` (included in `-Wconversion`) can be used to warn about this. As you now know it is not part of `-Wall` or `-Wextra`. – pmg Mar 02 '11 at 16:12
  • That's crazy: no warning at all, would it be about the %d or the negative... If that's not FAIL, I don't know what it is... – jokoon Mar 02 '11 at 17:59
  • 3
    @Joachim: I don't see a reason the compiler should issue warnings for perfectly reasonable use of the language. It's a shame that the specifics of C's integer semantics are not usually introduced early in books and courses, but that's not really an excuse for tailoring the compiler's preferred warning level to people who don't know the language they're programming in... – R.. GitHub STOP HELPING ICE Mar 03 '11 at 00:54
  • 4
    @R: It's actually a shame that the *GCC* compiler lies to the *user* with `-Wall`, since it doesn't turn on "all" warnings. – user541686 Mar 03 '11 at 07:49

14 Answers14

71

The -12 to the right of your equals sign is set up as a signed integer (probably 32 bits in size) and will have the hexadecimal value 0xFFFFFFF4. The compiler generates code to move this signed integer into your unsigned integer x which is also a 32 bit entity. The compiler assumes you only have a positive value to the right of the equals sign so it simply moves all 32 bits into x. x now has the value 0xFFFFFFF4 which is 4294967284 if interpreted as a positive number. But the printf format of %d says the 32 bits are to be interpreted as a signed integer so you get -12. If you had used %u it would have printed as 4294967284.

In either case you don't get what you expected since C language "trusts" the writer of code to only ask for "sensible" things. This is common in C. If you wanted to assign a value to x and were not sure whether the value on the right side of the equals was positive you could have written unsigned int x = abs(-12); and forced the compiler to generate code to take the absolute value of a signed integer before moving it to the unsigned integer.

Joseph
  • 117,725
  • 30
  • 181
  • 234
Tim Oltrogge
  • 742
  • 5
  • 4
  • 15
    Much of this is technically wrong. In `unsigned int x = -12;`, the `-12` is an integer constant expression of type `int`. That is converted to `unsigned int` by repeatedly adding (or subtracting) `UINT_MAX + 1` to the value until a value between 0 and `UINT_MAX` (both inclusive) is obtained, that value is the result of the conversion. On ones' complement or sign-and-magnitude machines, that changes the bit pattern of the value; on the more common two's complement machines, it's just a different interpretation of the same bit-pattern. – Daniel Fischer Jun 24 '12 at 19:30
  • 8
    That part, however, is completely defined by the standard, and after `unsigned int x = -12;`, the value of `x` must always be `UINT_MAX - 11`. The next line, `printf("%d", x);` invokes undefined behaviour, since the `%d` conversion specifier requires a signed `int` and the value of `x` is _not_ representable in both `unsigned int` and `int`. The common behaviour is of course the reinterpretation of the bit pattern, on ones' complement machines that would in this case yield `-11`, on sign-and-magnitude with 32-bit `int`s, `-2147483636`. And on two's complement machines the observed behaviour. – Daniel Fischer Jun 24 '12 at 19:30
  • @DanielFischer Why is the value of `x` not representable in `unsigned int` since the value is less than `UINT_MAX`? – ajay Feb 25 '14 at 10:33
  • 1
    @ajay The value of `x` is representable as an `unsigned int`, but not as an `int` (unless the implementation is so weird that its `unsigned int`s have a padding bit so the range is a subrange of the `int` range). Printing an `unsigned int` with the `%d` conversion specifier is undefined behaviour unless the value is representable in _both_ types. – Daniel Fischer Feb 25 '14 at 11:18
  • Makes sense to me. Personal anecdote: I tried to do remove the sign on an integer by casting it to an unsigned int and having it store the new value. Came to find out, of course, that that's not how C++ works. –  Nov 11 '16 at 01:37
33

The int is unsinged, but you've told printf to look at it as a signed int.

Try

unsigned int x = -12; printf("%u", x);

It won't print "12", but will print the max value of an unsigned int minus 11.

Exercise to the reader is to find out why :)

Binary Worrier
  • 50,774
  • 20
  • 136
  • 184
21

Passing %d to printf tells printf to treat the argument as a signed integer, regardless of what you actually pass. Use %u to print as unsigned.

Erik
  • 88,732
  • 13
  • 198
  • 189
7

It all has to do with interpretation of the value.

If you assume 16 bit signed and unsigned integers, then here some examples that aren't exactly correct, but demonstrate the concept.

0000 0000 0000 1100 unsigned int, and signed int value 12

1000 0000 0000 1100 signed int value -12, and a large unsigned integer.

For signed integers, the bit on the left is the sign bit. 0 = positive 1 = negative

For unsigned integers, there is no sign bit. the left hand bit, lets you store a larger number instead.

So the reason you are not seeing what you are expecting is that.

unsigned int x = -12, takes -12 as an integer, and stores it into x. x is unsigned, so what was a sign bit, is now a piece of the value.

printf lets you tell the compiler how you want a value to be displayed.

%d means display it as if it were a signed int. %u means display it as if it were an unsigned int.

c lets you do this kind of stuff. You the programmer are in control.

Kind of like a firearm. It's a tool. You can use it correctly to deal with certain situations, or incorrectly to remove one of your toes.

one possibly useful case is the following

unsigned int allBitsOn = -1;

That particular value sets all of the bits to 1

1111 1111 1111 1111

that can be useful sometimes.

EvilTeach
  • 28,120
  • 21
  • 85
  • 141
4
printf('%d', x); 

Means print a signed integer. You'll have to write this instead:

printf('%u', x);

Also, it'll still not print "12", it's going to be "4294967284".

Linus Kleen
  • 33,871
  • 11
  • 91
  • 99
3

They do store positive values. But you're outputting the (very high) positive value as a signed integer, so it gets re-interpreted again (in an implementation-defined fashion, I might add).

Use the format flag "%u instead.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
3

Your program has undefined behavior because you passed the wrong type to printf (you told it you were going to pass an int but you passed an unsigned int). Consider yourself lucky that the "easiest" thing for the implementation to do was just silently print the wrong value and not jump to some code that does something harmful...

R.. GitHub STOP HELPING ICE
  • 208,859
  • 35
  • 376
  • 711
  • 2
    passing in an unsigned integer where a variable argument of corresponding signed type is expected is actually well-defined if the value is representable in both types (see C99 7.15.1.1 §2); however, this is not the case here, but any reasonable implementation of the C language will just translate the value via 'type punning' (see footnote 82 on page 73) – Christoph Mar 03 '11 at 12:13
2

What you are missing is that the printf("%d",x) expects x to be signed, so although you assign -12 to x it is interpreted as 2's complement which would be a very large number. However when you pass this really large number to printf it interprets it as signed thus correctly translating it back to -12.

The correct syntax to print a unsigned in print f is "%u" - try this and see what it does!

Elemental
  • 7,365
  • 2
  • 28
  • 33
1

int and unsigned int are used to allocate a number of bytes to store a value nothing more.

The compiler should give warnings about signed mismatching but it really does not affect the bits in the memory that represent the value -12.

%x, %d, %u etc tells the compiler how to interrupt a number of bits when you print them.

trevor
  • 11
  • 1
1

When you are trying to display the int value you are passing it to a (int) argument and not a (unsigned int) argument and that causes it to print -12 and not 4294967284. Integers are stored in hexadecimal format and -12 for int is the same as 4294967284 for unsigned int in hexadecimal format.. That is why "%u" prints the right value you want and not "%d".. It depends on your argument type..GOOD LUCK!

Vern01
  • 11
  • 2
1

The assignment of a negative value to an unsigned int does not compute the absolute value of the negative: it interprets as an unsigned int the binary representation of the negative value, i.e., 4294967284 (2^32 - 12).

printf("%d") performs the opposite interpretation. This is why your program displays -12.

mouviciel
  • 66,855
  • 13
  • 106
  • 140
  • 1
    It doesn't just reinterpret the representation formally, and one ones' complement or sign-and-magnitude machines it factually doesn't. The signed value is converted to `unsigned` by taking its remainder modulo `UINT_MAX + 1`, so `unsigned int x = -12;` sets `x` to `UINT_MAX - 11` regardless of how signed integers are represented. – Daniel Fischer Jun 24 '12 at 19:35
1

The -12 is in 16-bit 2's compliment format. So do this: if (x & 0x8000) { x = ~x+1; } This will convert the 2's compliment -ve number to the equivalent +ve number. Good luck.

0

When the compiler implicitly converts -12 to an unsigned integer, the underlying binary representation remains unaltered. This conversion is purely semantic. The sign bit of the two's complement integer becomes the most significant bit of the unsigned integer. Thus when printf treats the unsigned integer as a signed integer with %d, it will see -12.

EMR
  • 44
  • 3
0

In general context when only positive numbers can be stored, negative numbers are not stored explicitly but their 2's complement is stored. In the same way here, the 2's complement of -12 will be stored in 'x' and you use %u to get it.

Sumit Singh
  • 15,743
  • 6
  • 59
  • 89
King
  • 143
  • 1
  • 1
  • 8