Home > OS >  Why does left-shifting an integer by 24-bit yield the wrong result?
Why does left-shifting an integer by 24-bit yield the wrong result?

Time:12-06

I tried left-shifting a 32-bit integer by 24:

char *int_to_bin(int num) {
    int i = 0;
    static char bin[64];
   
    while (num != 0) {
        bin[i] = num % 2   48;
        num /= 2;
        i  ;
    }
    bin[i] = '\0';
    return (bin);
}

int main() {
    int number = 255;
    printf("number: %s\n", int_to_bin(number));
    printf("shifted number: %s\n", int_to_bin(number << 24));
    return 0;
}

OUTPUT:

number: 11111111
shifted number: 000000000000000000000000/

and i left-shift with 23-bit it yields this result:

0000000000000000000000011111111

Well Why is it like that and what's the matter with '/' at the end of the wrong result?

CodePudding user response:

Two things:

  • If number has the value 255 then number << 24 has the numerical value 4278190080, which overflows a 32-bit signed integer whose largest possible value is 2147483647. Signed integer overflow is undefined behavior in C, so the result could be anything at all.

    What probably happens in this case is that the result of the shift is negative. When num is negative then num % 2 may take the value -1, so you store character 47 in the string, which is /.

    Bit shifting math is usually better to do with unsigned types, where overflow is well-defined (it wraps around and bits just shift off the left and vanish) and num % 2 can only be 0 or 1. (Or write num & 1 instead.)

  • Your int_to_bin routine puts the least-significant bits at the beginning of the string (on the left), so the result is backwards from the way people usually write numbers (with the least-significant bits on the right). You may want to rewrite it.

CodePudding user response:

Shift works fine, you simply print it from the wrong direction.

char *int_to_bin(char *buff, int num)
{
    unsigned mask = 1U << (CHAR_BIT * sizeof(num) - 1);
    char *wrk = buff;
    for(; mask; mask >>= 1)
    {
        *wrk   = '0'   !!((unsigned)num & mask);
    }
    *wrk = 0;
    return buff;
}

int main()
{
    char buff[CHAR_BIT * sizeof(int)   1];
    int number = 255;
    printf("number: %s\n", int_to_bin(buff, number));
    printf("shifted number: %s\n", int_to_bin(buff, number << 24));
    return 0;
}

Shifting signed integers left is OK, but the right shift is implementation-defined. Many systems use arithmetic shift right and the result is not the same as using the bitwise one:

https://godbolt.org/z/e7f3shxd4

  • Related