Left shift bits in c

I did some silly bit manipulation tests and I found this problem. I am executing this code:

int main(){ unsigned int i; for (i=1; i<34; i++) { unsigned long temp = i; unsigned long mul = 1; unsigned long val; unsigned long one = 1; // Way 1 while (temp--) mul = mul << one; // Way 2 val = (one<<i); printf(" \n 1<<%i \n mul: 0x%X , val: 0x%X\n",i, mul, val); } } 

Of course, I know that for i> 31 an overflow will occur. I think that both parts of the code (way1 and way2) should output the same result. But I get this (at the end):

  /* ... correct results from i=1 to i=31 ... */ 1<<30 mul: 0x40000000 , val: 0x40000000 1<<31 mul: 0x80000000 , val: 0x80000000 1<<32 mul: **0x0** , val: **0x1** 1<<33 mul: **0x0** , val: **0x2** 

Why, if both instructions are shifted to the left, does the program produce different outputs? It seems that part way2 is doing a circular shift, but I donโ€™t know why, I really think that โ€œmulโ€ always gets the right value.

I am compiling an Intel 32bits machine, gcc version 4.4.7

+5
source share
4 answers

When

 val = (one<<i); 

when i gets greater than or equal to 32, the behavior is undefined.

However, in the case of

 while (temp--) mul = mul << one; 

for shifts greater than 32, it will shift zero and the result will be determined (zero).

+2
source

Perhaps because this behavior is undefined? According to ยง6.5.7 :

If the value of the correct operand is negative or greater than or equal to the width of the advanced left operand, the behavior is undefined.

+9
source

When you do this:

 val = (one<<i); 

You are doing a single left shift by i . If i greater than 31, this leads to undefined behavior , which means that the result may or may not be what you expect.

From section 6.5.7.3 of standard C :

Integer promotions are executed for each of the operands. The result type is the result of the advanced left operand. If the value of the right operand is negative or greater than or equal to the width of the advanced left operand, the behavior is undefined.

However, when you do this:

 while (temp--) mul = mul << one; 

You are shifting left 1 i times. This is well defined, so it gives you the expected value.

In addition, you use %X to print long , when you must use %lX . It also causes undefined behavior.

0
source

When I compile your code using -Wall , I received complaints:

 BASH> gcc -Wall left-shift.c left-shift.c: In function 'main': left-shift.c:21:12: warning: format '%X' expects argument of type 'unsigned int', but argument 3 has type 'long unsigned int' [-Wformat=] printf(" \n 1<<%i \n mul: 0x%X , val: 0x%X\n",i, mul, val); ^ left-shift.c:21:12: warning: format '%X' expects argument of type 'unsigned int', but argument 4 has type 'long unsigned int' [-Wformat=] 

So I changed printf to

 printf(" \n 1<<%i \n mul: 0x%lX , val: 0x%lX\n",i, mul, val); 

With this change, "mul" and "val" show the same results:

  1<<30 mul: 0x40000000 , val: 0x40000000 1<<31 mul: 0x80000000 , val: 0x80000000 1<<32 mul: 0x100000000 , val: 0x100000000 1<<33 mul: 0x200000000 , val: 0x200000000 

System Information:

 BASH> gcc --version gcc (Ubuntu 5.3.1-14ubuntu2.1) 5.3.1 20160413 Copyright (C) 2015 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. BASH> uname -a Linux bm-pc-ubuntu 4.4.0-24-generic #43-Ubuntu SMP Wed Jun 8 19:27:37 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux BASH> lsb_release --all No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 16.04 LTS Release: 16.04 Codename: xenial 
-3
source

All Articles