Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Shift Operators in C++

If the value after the shift operator is greater than the number of bits in the left-hand operand, the result is undefined. If the left-hand operand is unsigned, the right shift is a logical shift so the upper bits will be filled with zeros. If the left-hand operand is signed, the right shift may or may not be a logical shift (that is, the behavior is undefined).

Can somebody explain me what the above lines mean??

like image 269
Jony Avatar asked Mar 30 '10 18:03

Jony


3 Answers

It doesn't matter too much what those lines mean, they are substantially incorrect.

"If the value after the shift operator is greater than the number of bits in the left-hand operand, the result is undefined."

Is true, but should say "greater than or equal to". 5.8/1:

... the behavior is undefined if the right hand operand is negative, or greater than or equal to the length in bits of the promoted left operand.

Undefined behavior means "don't do it" (see later). That is, if int is 32 bits on your system, then you can't validly do any of the following:

int a = 0; // this is OK
a >> 32;   // undefined behavior
a >> -1;   // UB
a << 32;   // UB
a = (0 << 32); // Either UB, or possibly an ill-formed program. I'm not sure.

"If the left-hand operand is unsigned, the right shift is a logical shift so the upper bits will be filled with zeros."

This is true. 5.8/3 says:

If E1 has unsigned type or if E1 has a signed type and a nonnegative value, the result is the integral part of the quotient of E1 divided by the quantity 2 raised to the power E2

if that makes any more sense to you. >>1 is the same as dividing by 2, >>2 dividing by 4, >>3 by 8, and so on. In a binary representation of a positive value, dividing by 2 is the same as moving all the bits one to the right, discarding the smallest bit, and filling in the largest bit with 0.

"If the left-hand operand is signed, the right shift may or may not be a logical shift (that is, the behavior is undefined)."

First part is true (it may or may not be a logical shift - it is on some compilers/platforms but not others. I think by far the most common behaviour is that it is not). Second part is false, the behavior is not undefined. Undefined behavior means that anything is permitted to happen - a crash, demons flying out of your nose, a random value, whatever. The standard doesn't care. There are plenty of cases where the C++ standard says behavior is undefined, but this is not one of them.

In fact, if the left hand operand is signed, and the value is positive, then it behaves the same as an unsigned shift.

If the left hand operand is signed, and the value is negative, then the resulting value is implementation-defined. It isn't allowed to crash or catch fire. The implementation must produce a result, and the documentation for the implementation must contain enough information to define what the result will be. In practice, the "documentation for the implementation" starts with the compiler documentation, but that might refer you implicitly or explicitly to other docs for the OS and/or the CPU.

Again from the standard, 5.8/3:

If E1 has signed type and negative value, the resulting value is implementation-defined.

like image 73
Steve Jessop Avatar answered Sep 24 '22 15:09

Steve Jessop


I'm assuming you know what it means by shifting. Lets say you're dealing with a 8-bit chars

unsigned char c;
c >> 9;
c >> 4;
signed char c;
c >> 4;

The first shift, the compiler is free to do whatever it wants, because 9 > 8 [the number of bits in a char]. Undefined behavior means all bets are off, there is no way of knowing what will happen. The second shift is well defined. You get 0s on the left: 11111111 becomes 00001111. The third shift is, like the first, undefined.

Note that, in this third case, it doesn't matter what the value of c is. When it refers to signed, it means the type of the variable, not whether or not the actual value is greater than zero. signed char c = 5 and signed char c = -5 are both signed, and shifting to the right is undefined behavior.

like image 27
Dennis Zickefoose Avatar answered Sep 23 '22 15:09

Dennis Zickefoose


If the value after the shift operator is greater than the number of bits in the left-hand operand, the result is undefined.

It means (unsigned int)x >> 33 can do anything[1].

If the left-hand operand is unsigned, the right shift is a logical shift so the upper bits will be filled with zeros.

It means 0xFFFFFFFFu >> 4 must be 0x0FFFFFFFu

If the left-hand operand is signed, the right shift may or may not be a logical shift (that is, the behavior is undefined).

It means 0xFFFFFFFF >> 4 can be 0xFFFFFFFF (arithmetic shift) or 0x0FFFFFFF (logical shift) or anything-allowed-by-physical-law, i.e. the result is undefined.

[1]: on 32-bit machine with a 32-bit int.

like image 29
kennytm Avatar answered Sep 20 '22 15:09

kennytm