Bitwise Leftshift (<<) strange behavior

后端 未结 6 868
鱼传尺愫
鱼传尺愫 2021-01-15 13:41

gcc bitwise Leftshift (<<) strange behavior. Here is my code:

#include 
#include 

void foo(in         


        
相关标签:
6条回答
  • 2021-01-15 13:59

    I finally figure out a work-around solution, at least make the output identical.

    #include <stdio.h>
    #include <string.h>
    
    void foo(int n){
      printf("1<<32:%d\n", 1<<32);
      printf("1<<(32-n):%d\n", (1<<(31-n))<<1);
    }
    
    int main(){
        foo(0);
    }
    
    0 讨论(0)
  • 2021-01-15 14:01

    Since you're shifting 32-bit ints, shifting by 32 bits would result in a zero value. However, the bitshift operation of the CPU can only shift by 0 to 31 bits as anything else is generally not useful and would only complicate the computation.

    The reason that the first example, 1<<32, seems to work, is that the compiler optimises this to 0 at compile time, while also printing a warning. The other example, 1<<(32-n), however, has a shift value that cannot be determined at compile time (thus no warning either). Instead, the CPU uses the result of the subtraction 32 - n == 32 for its shift operation, but the CPU only takes the five lowest bits and thus overflows to 0, and the result is 1 << 0 == 1.

    To work around this, you will have to either special-case n == 0, use a wider data type, or simply use fewer bits.

    0 讨论(0)
  • 2021-01-15 14:06

    According to the C standard ISO 9899:1999 Chapter 6.5.7 Bitwise shift operators:

    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.

    It is strange that the compiler treats the two expressions differently. But since this causes undefined behavior anyway, it's not a problem. What you need to do is to check the operands before evaluation to make sure it's a valid expression.

    0 讨论(0)
  • 2021-01-15 14:16

    Shifting by a value that is equal or greater than the width of the promoted type of the left operand is undefined behaviour, so you must specifically test for and avoid this. In addition, a left-shift of a signed type that results in overflow is also undefined behaviour, so you need to also avoid a shift of 31, too:

    printf("1<<(32-n):%d\n", (n > 1 && n < 33) ? 1 << (32-n) : 0);
    

    This particular expression uses 0 for the cases that are otherwise undefined, but you can handle those differently if you need to.

    0 讨论(0)
  • 2021-01-15 14:17

    Don't be surprised. you're dealing with a 32bit int, so when you do 1<<32, you've shifted that set bit right off the end of the int and zeroed out the whole thing.

    e.g. in binary:

        33222222 22211111 11111000 00000000
        10987654 32109876 54321098 76543210
     1: 00000000 00000000 00000000 00000001
    
       ^--- position #32
    
    0 讨论(0)
  • 2021-01-15 14:19

    gcc is telling you what the problem is with the warning:

    main.c:5:3: warning: left shift count >= width of type [enabled by default]
    

    Your shifts need to be less than the size of the type otherwise it is undefined behavior. The C99 draft standard section 6.5.7 Bitwise shift operators paragraph 3 says:

    [...]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.

    Why is the first printf different from the second one? If you build using -fdump-tree-original you will see the following code generated for foo:

    printf ((const char * restrict) "1<<32:%d\n", 0);
    printf ((const char * restrict) "1<<(32-n):%d\n", 1 << 32 - n);
    

    It seems the first case it being optimized away to 0 which is consistent with undefined behavior, the compiler can do anything, including behavior that appears to work.

    0 讨论(0)
提交回复
热议问题