tags:

views:

133

answers:

4

Hi ! I am trying to figure out how exactly arithmetic bit-shift operators work in C, and how it will effect signed 32 bit integer.

To make things simple, lets say we work within one byte(8 bits) -

x = 1101.0101 
MSB[ 1101.0101 ]LSB

Reading other posts on Stack & some websites, I found that: << will shit toward MSB(to the left, in my case ), and fill "empty" LSB bits with 0s

and >> will shift toward LSB (to the right, in my case), and fill "empty" bits with MS bit

so, x = x << 7 will result in moving LSB to MSB, and setting everything to 0s

1000.0000

Now, lest say I would >> 7, last result This would result in [0000.0010] ? I am right ???

Thanks ! (Please, check if I was right about my assumptions about shift operators)

Edit: I just test on my machine,

int x = 1;   //000000000......01  

x = x << 31; //100000000......00  

x = x >> 31; //111111111......11 (everything is filled with 1s !!!!!) - Why ????  
+2  A: 

Right shift of a negative signed number has implementation-defined behaviour.

If your 8 bits are meant to represent a signed 8 bit value (as you're talking about a "signed 32 bit integer" before switching to 8 bit examples) then you have a negative number. Shifting it right may fill "empty" bits with the original MSB (i.e. perform sign extension) or it may shift in zeroes, depending on platform and/or compiler.

(Implementation-defined behaviour means that the compiler will do something sensible, but in a platform-dependent manner; the compiler documentation is supposed to tell you what.)


A left shift, if the number either starts out negative, or the shift operation would shift a 1 either to or beyond the sign bit, has undefined behaviour (as do most operations on signed values which cause an overflow).

(Undefined behaviour means that anything at all could happen.)


The same operations on unsigned values are well-defined in both cases: the "empty" bits will be filled with 0.

Matthew Slattery
+4  A: 

Bitwise shift operations are not defined for negative values

for '<<'

6.5.7/4 [...] If E1 has a signed type and nonnegative value, and E1×2E2 is representable in the result type, then that is the resulting value; otherwise, the behavior is undefined.

and for '>>'

6.5.7/5 [...] If E1 has a signed type and a negative value, the resulting value is implementation- defined.

It's a waste of time to study the behaviour of these operations on signed numbers on a specific implementation, because you have no guarantee it will work the same way on any other implementation (an implementation is, for example, you compiler on your computer with your specific commad-line parameters).

Considering only non-negative values, the effect of left shifting by 1 (expression << 1) is the same as multpliying the expression by 2 (provided expression * 2 does not overflow) and the effect of right shifting by 1 (expression >> 1) is the same as dividing by 2.

pmg
I think you quoted the wrong bit of the standard; your current quote is talking about negative RHS, i.e. cases like `x >> -1`.
Porculus
@Porculus: yes, thank you. Post changed while you were writing your comment
pmg
A: 

You'll usually want to write the LSB at the right end, just like you do in usual arithmetics - twelve is 12, not 21. This will make things easier because << will shift to the MSB, and because the MSB is at the left end of your binary number, the mnemonics (<< = left and >> = right) work correctly.

As for the shifting operation itself:

(10101011 << 1) = 01010110
(10101011 << 2) = 10101100

and so on.

vwegert
You're describing rotation, not shifting. << does not do rotation.
Porculus
@Porculus: Thanks, fixed it. Shouldn't answer C questions while working with a different language...
vwegert
+1  A: 

As others said shift of negative value is implementation-defined.

Most of implementations treat signed right shift as floor(x/2N) by filling shifted in bits using sign bit. It is very convenient in practice, as this operation is so common. On the other hand if you will shift right unsigned integer, shifted in bits will be zeroed.

Looking from machine side, most implementations have two types of shift-right instructions:

  1. An 'arithmetic' shift right (often having mnemonic ASR or SRA) which works as me explained.

  2. A 'logic' shift right (oftem having mnemonic LSR or SRL or SR) which works as you expect.

Most of compilers utilize first for signed types and second for unsigned ones. Just for convenience.

Vovanium