cgccbit-manipulation64-bitbit-shift

Inconsistent bitwise shifting result in C code


I'm writing a C program and need to create a bitwise mask that could potentially fill a computer word (64 or 32 bits) with all 1s. I found a bug in my code but I can't make sense of it because when I run the operation using literal values it gives the expected value, but when I store the parts in variables and do the same operation I get a different result.

#include <stdio.h>
#include <stdint.h>

#define min(a,b) (((a) < (b)) ? (a) : (b))

typedef uint64_t WORD;

int main() {
    
    WORD one = 1;
    
    printf("mask0=%lu\n", (one<<64) - one);
    
    uint32_t shift_amt = 2;
    uint32_t pos_in_word = 62;
    uint32_t my_min = min((uint32_t)64, pos_in_word + shift_amt);
    WORD mask = (one<<my_min) - one;
    
    printf("one=%lu, my_min=%d pos_in_word=%d, shift_amt=%d, mask1=%lu\n", 
            one, my_min, pos_in_word, shift_amt, mask);

    return 0;
}

Building with command: gcc -Wall -std=c99 main.c -lm -o main under Linux Ubuntu.

I expect the value to be 18446744073709551615 (all 1s) in both cases. In the first case, mask0=18446744073709551615. In the second case, mask1=0. I don't understand how. I'd really appreciate some help please.


Solution

  • There are multiple issues in your code:

    Note that (one << 64) would be 0 if the full shift did occur, so you could just write (WORD)0 instead. In your program, you compute one << my_min, which has undefined behavior if my_min is greater or equal to 64, so you just cannot use this expression, you must add a test:

    WORD mask = (my_min >= 64 ? (WORD)0 : one << my_min) - one;
    

    Also note that printing bit patterns in hexadecimal is much more readable.

    Here is a modified version:

    #include <stdio.h>
    #include <stdint.h>
    
    uint32_t min_u32(uint32_t a, uint32_t b) { return a < b ? a : b; }
    
    typedef uint64_t WORD;
    
    int main(void) {
        
        WORD one = 1;
        
        printf("mask0=%#"PRIx64"\n", -one);
        
        uint32_t shift_amt = 2;
        uint32_t pos_in_word = 62;
        uint32_t my_min = min_u32(64, pos_in_word + shift_amt);
        WORD mask = (my_min >= 64 ? (WORD)0 : one << my_min) - one;
        
        printf("one=%#"PRIx64", my_min=%"PRIu32" pos_in_word=%"PRIu32", shift_amt=%"PRIu32", mask1=%#"PRIx64"\n", 
                one, my_min, pos_in_word, shift_amt, mask);
    
        return 0;
    }
    

    For your purpose, filling a word with all one bits, here are 2 simple portable methods: