简体   繁体   中英

Left shift bits in c

I have been making some stupid test about bits manipulation, and I found this issue. I execute 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 when i>31, an overflow will be produced. I think that both parts of 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 left shifts, the program produces different outputs? It seems that the part way2 produces a round shift, but I don't know why, I really think that "mul" gets always the correct value.

I compile under a Intel 32bits machine, gcc version 4.4.7

Probably because that's undefined behaviour? According to §6.5.7 :

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

In case of

val = (one<<i);

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

However, in case of

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

for shifts more than 32, it will shift zero and the result is defined (zero).

When you do this:

val = (one<<i);

You're doing a single left shift by i . If i is greater than 31, this results in undefined behavior , meaning the result may or may not be what you expect.

From section 6.5.7.3 of the C standard :

The integer promotions are performed on each of the operands. The type of the result is that of the promoted left operand. If the value of the right operand is negative or is greater than or equal to the width of the promoted left operand, the behavior is undefined.

However, when you do this:

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

You're doing a left shift by 1 i times. This is well defined, so it gives you the value you expect.

Also, you're using %X to print a long when you should be using %lX . This causes undefined behavior as well.

When I compile Your code with -Wall I got 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" shows 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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM