Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Right shifting negative numbers in C

I have C code in which I do the following.

int nPosVal = +0xFFFF;   // + Added for ease of understanding int nNegVal = -0xFFFF;   // - Added for valid reason 

Now when I try

printf ("%d %d", nPosVal >> 1, nNegVal >> 1); 

I get

32767 -32768 

Is this expected?

I am able to think something like

65535 >> 1 = (int) 32767.5 = 32767 -65535 >> 1 = (int) -32767.5 = -32768 

That is, -32767.5 is rounded off to -32768.

Is this understanding correct?

like image 675
Alphaneo Avatar asked Dec 07 '09 05:12

Alphaneo


People also ask

Can you right shift negative number in C?

Important Points : The left shift and right shift operators should not be used for negative numbers. The result of is undefined behaviour if any of the operands is a negative number.

What is right shift of a negative number?

Right Shifts For signed numbers, the sign bit is used to fill the vacated bit positions. In other words, if the number is positive, 0 is used, and if the number is negative, 1 is used. The result of a right-shift of a signed negative number is implementation-dependent.

Is shifting right negative or positive?

Horizontal Shift If k is positive, then the graph will shift left. If k is negative, then the graph will shift right.

Can shift operator be applied to negative numbers?

Operator >> called Signed right shift, shift all the bits to right a specified number of times. Important is >> fills leftmost sign bit (Most Significant Bit MSB) to leftmost bit the after shift. This is called sign extension and serves to preserve the sign of negative numbers when you shift them right.


Video Answer


2 Answers

It looks like your implementation is probably doing an arithmetic bit shift with two's complement numbers. In this system, it shifts all of the bits to the right and then fills in the upper bits with a copy of whatever the last bit was. So for your example, treating int as 32-bits here:

nPosVal = 00000000000000001111111111111111 nNegVal = 11111111111111110000000000000001 

After the shift, you've got:

nPosVal = 00000000000000000111111111111111 nNegVal = 11111111111111111000000000000000 

If you convert this back to decimal, you get 32767 and -32768 respectively.

Effectively, a right shift rounds towards negative infinity.

Edit: According to the Section 6.5.7 of the latest draft standard, this behavior on negative numbers is implementation dependent:

The result of E1 >> E2 is E1 right-shifted E2 bit positions. If E1 has an unsigned type or if E1 has a signed type and a nonnegative value, the value of the result is the integral part of the quotient of E1 / 2E2. If E1 has a signed type and a negative value, the resulting value is implementation-defined.

Their stated rational for this:

The C89 Committee affirmed the freedom in implementation granted by K&R in not requiring the signed right shift operation to sign extend, since such a requirement might slow down fast code and since the usefulness of sign extended shifts is marginal. (Shifting a negative two’s complement integer arithmetically right one place is not the same as dividing by two!)

So it's implementation dependent in theory. In practice, I've never seen an implementation not do an arithmetic shift right when the left operand is signed.

like image 112
Boojum Avatar answered Sep 19 '22 16:09

Boojum


No, you don't get fractional numbers like 0.5 when working with integers. The results can be easily explained when you look at the binary representations of the two numbers:

      65535: 00000000000000001111111111111111      -65535: 11111111111111110000000000000001 

Bit shifting to the right one bit, and extending at the left (note that this is implementation dependant, thanks Trent):

 65535 >> 1: 00000000000000000111111111111111 -65535 >> 1: 11111111111111111000000000000000 

Convert back to decimal:

 65535 >> 1 = 32767 -65535 >> 1 = -32768 
like image 32
Mark Byers Avatar answered Sep 21 '22 16:09

Mark Byers