5

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.

1 Answers1

5
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.

ouah
  • 142,963
  • 15
  • 272
  • 331
  • @coffeeholic : No `int` can be max 4 byte wide. On 64-bit system `long` is 8-byte wide. Check it using `sizeof`. – 0xF1 Feb 03 '14 at 11:02
  • @coffeeholic `int` is at least 16-bit wide and is very likely to be 32-bit wide in a 64-bit system (except in some exotic Cray system where it is 64-bit). – ouah Feb 03 '14 at 11:02
  • w00t, you're right, int is just 4 bytes wide even on a 64 bit system. Looks like I'm not the brightest bulb in the barrel. – coffeeholic Feb 03 '14 at 11:03
  • No, it's up to the implementation, but `int` is indeed 4 bytes on Visual Studio compiling for x64. – James Hopkin Feb 03 '14 at 11:05
  • I am sorry for my obvious, blatant and inexcusable lack of a deeper understanding of C that lead to this question. – coffeeholic Feb 03 '14 at 11:05
  • Basically `1 << 31` is negative (as a 4-byte int), which causes the upper 32-bits to be ones when it's promoted to 64-bit for ORing. – Dmitri Feb 03 '14 at 11:12