Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

why does left shift with variables generate different result from that with constant?

Tags:

c++

bit-shift

After compiling the code below, i got a weird result, a =1 while b =0. Could anyone explain what's going on behind the scene?

#include<iostream>
using namespace std;

int main(){

    int n=32; 
    int a=1<<n;  //turns out a=1
    int b=1<<32; //turns out b=0
    cout<<"a="<<a<<endl;
    cout<<"b="<<b<<endl;
}
like image 799
JDein Avatar asked May 13 '13 05:05

JDein


2 Answers

Because you are invoking undefined behavior. Shifting more bits than exist in the type is not defined to have any particular behavior.

See http://blog.llvm.org/2011/05/what-every-c-programmer-should-know.html?m=1

Oversized Shift Amounts: Shifting a uint32_t by 32 or more bits is undefined. My guess is that this originated because the underlying shift operations on various CPUs do different things with this: for example, X86 truncates 32-bit shift amount to 5 bits (so a shift by 32-bits is the same as a shift by 0-bits), but PowerPC truncates 32-bit shift amounts to 6 bits (so a shift by 32 produces zero). Because of these hardware differences, the behavior is completely undefined by C (thus shifting by 32-bits on PowerPC could format your hard drive, it is not guaranteed to produce zero). The cost of eliminating this undefined behavior is that the compiler would have to emit an extra operation (like an 'and') for variable shifts, which would make them twice as expensive on common CPUs.

like image 36
StilesCrisis Avatar answered Nov 12 '22 11:11

StilesCrisis


The standard does not define, or rather, it defines it as "undefined behaviour", what happens in the case left shift beyond the size of the integer type. [The reason for this undefined behaviour is that different hardware may or may not behave the same given, for example, a 32-bit shift to the left].

In the first case [at least without optimization], the compiler generates instructions to calculate 1 << 32 - which on x86 turns into 1 << (32 & 31) which is the same as 1 << 0 - thus you get 1.

In the second case, the compiler will calculate the value itself, which turns into an overflow, and gives zero.

It's quite likely (but not certain) that if you change the compiler options to optimize the code, it gives zero for both cases. And you are going to get the behaviour you want if you were to do a loop of smaller shifts (although you may find "intersting" behaviour with the fact that the number turns negative, so best to use unsigned for all shift operations, really).

like image 149
Mats Petersson Avatar answered Nov 12 '22 11:11

Mats Petersson