0

I have the following code in c:

 unsigned int a = 60; /* 60 = 0011 1100 */
 int c = 0;
 c = ~a; /*-61 = 1100 0011 */
 printf("c = ~a = %d\n", c );
 c = a << 2; /* 240 = 1111 0000 */
 printf("c = a << 2 = %d\n", c );

The first output is -61 while the second one is 240. Why the first printf computes the two's complement of 1100 0011 while the second one just converts 1111 0000 to its decimal equivalent?

loubas
  • 191
  • 2
  • 3
  • 15
  • 1
    @Babbleshack no it couldn't. It's about an assumption this person has made about the width of an int. – paddy Feb 03 '14 at 22:53
  • @paddy good job i made it a comment then. I should have spent more time thinking about the question sorry about that, ill delete it. – Babbleshack Feb 03 '14 at 22:54
  • 1
    `c` would have a value of `1111 1111 1111 1111 1111 1111 1100 0011`. `~` complements all the bits, not just those you list in the first line. – Hot Licks Feb 03 '14 at 22:56
  • 1
    As @paddy implied, your int is (probably) 4 bytes, not 1 byte. Therefore, `~a = 11111111 11111111 11111111 11000011`, but `a << 2 = 00000000 00000000 00000000 11110000`. – Blorgbeard Feb 03 '14 at 22:56
  • 2
    And note that `c`, the value being printed, is declared as signed, and the `%` format code is for signed printing, so `c` is naturally interpreted as a signed 2's complement number. – Hot Licks Feb 03 '14 at 22:59

2 Answers2

6

You have assumed that an int is only 8 bits wide. This is probably not the case on your system, which is likely to use 16 or 32 bits for int.

In the first example, all the bits are inverted. This is actually a straight inversion, not two's complement:

1111 1111 1111 1111 1111 1111 1100 0011  (32-bit)
                    1111 1111 1100 0011  (16-bit)

In the second example, when you shift it left by 2, the highest-order bit is still zero. You have misled yourself by depicting the numbers as 8 bits in your comments.

0000 0000 0000 0000 0000 0000 1111 0000  (32-bit)
                    0000 0000 1111 0000  (16-bit)
paddy
  • 60,864
  • 6
  • 61
  • 103
  • 3
    Not only on older systems. On most 8- and 16 bit systems nowadays, it is still 16 bits, e. g. on many µCs. – glglgl Feb 03 '14 at 22:58
0

Try to avoid doing bitwise operations with signed integers -- often it'll lead you into undefined behavior.

The situation here is that you're taking unsigned values and assigning them to a signed variable. For ~60 this is undefined behavior. You see it as -61 because the bit pattern ~60 is also the two's-complement representation of -61. On the other hand 60 << 2 comes out correct because 240 has the same representation both as a signed and unsigned integer.

Community
  • 1
  • 1
TypeIA
  • 16,916
  • 1
  • 38
  • 52
  • ITYM *ones* complement? – Paul R Feb 03 '14 at 22:58
  • @PaulR no, ~60 is the twos complement representation of -61 - it's the ones complement representation of -60 – Blorgbeard Feb 03 '14 at 23:00
  • @PaulR No... two's complement. Think about it, `~1` is `11111110` (sign extended for word size); that is -2. `~60` is -61. Reconsider your downvote? (Also, other downvoter please comment?) – TypeIA Feb 03 '14 at 23:00
  • Sorry - I misread it - I thought you were saying that `~` produces a twos complement result. – Paul R Feb 03 '14 at 23:01
  • 1
    It's not undefined behavior -- the bit operations are completely defined. The only thing "undefined" is whether the machine is 1s complement or 2s complement, and if that is known the behavior is completely defined. – Hot Licks Feb 03 '14 at 23:05
  • @HotLicks I thought storing an unsigned value which is too large to fit in its signed counterpart (I.e. its high bit is 1) was UB. – TypeIA Feb 03 '14 at 23:09
  • Only in the book. And your initial advice to "avoid doing bitwise operations with signed integers" is poor at best. There are many cases where this is useful, but you've got to know when. – Hot Licks Feb 03 '14 at 23:12
  • 1
    I think what happened here is you confused the part in the C standard that talks about right-shifting negative signed values, which is implementation dependent. Whereas left-shifting any signed value is well defined. – paddy Feb 04 '14 at 01:19
  • @paddy No, I'm talking about assigning the unsigned value `~60` overflowing when assigning it to a signed variable. I'm pretty sure [that is, in fact, U.B.](http://stackoverflow.com/questions/16188263/is-signed-integer-overflow-still-undefined-behavior-in-c) "Only in the book" is just a cop-out. "The book" says it's undefined and therefore it is, and relying on undefined behavior should NEVER be advocated, even if "almost all" compilers do it a certain way. – TypeIA Feb 04 '14 at 16:55