Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why do those two ways to set a variable to all 1s lead to different results?

Tags:

c

I recently discovered a discrepancy between two ways to set a variable to all 1s in C. Here is a small code sample to illustrate the odd behaviour on my 64 bit Linux system.

// compile with `gcc -o weird_shift_behaviour weird_shift_behaviour.c`

#include <stdio.h>

int main(void){
    long long foo = 0;
    long long bar = 0;
    int i;
    puts("<foo> will be set to all 1s by repeatedly shifting 1 to the left and OR-ing the result with <foo>.");
    puts("<bar> will be set to all 1s by repeatedly OR-ing it with 1 and shifting <bar> to the left one step.");
    for(i=0;i<8*(int)sizeof(long long)-1;++i){
        foo |= (1<<i);
        bar = bar<<1 | 1;
        printf("<i>: %02d <foo>: %016llx <bar>: %016llx \n",i,foo,bar);
    }
    return 0;
}

I do know that this is not the canonical way to set an integer type to all 1s in C, but I did try it nonetheless. Here is the interesting part of the output the sample program generates:

<i>: 29 <foo>: 000000003fffffff <bar>: 000000003fffffff 
<i>: 30 <foo>: 000000007fffffff <bar>: 000000007fffffff 
<i>: 31 <foo>: ffffffffffffffff <bar>: 00000000ffffffff 
<i>: 32 <foo>: ffffffffffffffff <bar>: 00000001ffffffff 

Why does this odd behaviour occur? I could not think of any reasonable explanation so far.

like image 344
coffeeholic Avatar asked Feb 03 '14 10:02

coffeeholic


1 Answers

1<<i

1 is of type int and 1 << 31 is undefined behavior when int is 32-bit wide.

From the C Standard:

(C99, 6.5.7p4) "The result of E1 << E2 is E1 left-shifted E2 bit positions; vacated bits are filled with zeros. [...] If E1 has a signed type and nonnegative value, and E1 x 2 ^ E2 is representable in the result type, then that is the resulting value; otherwise, the behavior is undefined."

To fix your issue, change 1<<i with 1ULL << i.

like image 107
ouah Avatar answered Oct 08 '22 23:10

ouah