Search code examples
cundefined-behaviorbit-shift

Why does shifting a variable by more than its width in bits zeroes out?


This question is inspired by other questions from StackOverflow. Today, while browsing StackOverflow, I've come across an issue of bitshifting a variable by a value k that is >= the width of that variable in bits. This means shifting a 32-bit int by 32 or more bit positions.

Left shift an integer by 32 bits

Unexpected C/C++ bitwise shift operators outcome

From these questions, it is obvious that if we attempt to shift a number by k bits that are >= the bit width of the variable, only the least-significant log2k bits are taken. For a 32-bit int, the least significant 5 bits are masked and taken to be the shift amount.

So in general, if w = width of the variable in bits, x >> k becomes x >> (k % w) For an int, this is x >> (k % 32).

The count is masked to five bits, which limits the count range to 0 to 31.

So I've written a small little program to observe the behavior that should theoretically be produced. I've written in comments the resulting shift amount % 32.

#include <stdio.h>
#include <stdlib.h>

#define PRINT_INT_HEX(x) printf("%s\t%#.8x\n", #x, x);

int main(void)
{
    printf("==============================\n");
    printf("Testing x << k, x >> k, where k >= w\n");

    int      lval = 0xFEDCBA98 << 32;
    //int      lval = 0xFEDCBA98 << 0;

    int      aval = 0xFEDCBA89 >> 36;
    //int      aval = 0xFEDCBA89 >> 4;

    unsigned uval = 0xFEDCBA89 >> 40;
    //unsigned uval = 0xFEDCBA89 >> 8;

    PRINT_INT_HEX(lval)
    PRINT_INT_HEX(aval)
    PRINT_INT_HEX(uval)

    putchar('\n');

    return EXIT_SUCCESS;
}

And the output does not match the expected behavior of the shift instructions!

==============================
Testing x << k, x >> k, where k >= w
lval    00000000 
aval    00000000
uval    00000000

=====================================================================

Actually I was a bit confused with Java. In C/C++, shifting an int by a number of bits greater than the bit width, could possibly be reduced k % w, but this is not guaranteed by the C standard. There is no rule which says that this kind of behavior should happen all the time. It is undefined behavior.

However, this is the case in Java. This is a rule of the Java programming language.

Bitshift operators description in Java language specification

Weird result of Java Integer left shift

java : shift distance for int restricted to 31 bits


Solution

  • The linked questions specifically state that shifting by an amount greater than the bit width of the type being shifted invokes undefined behavior, which the standard defines as "behavior, upon use of a nonportable or erroneous program construct or of erroneous data, for which this International Standard imposes no requirements"

    When you invoke undefined behavior, anything can happen. The program may crash, it may output strange results, or it may appear to work properly. Also, how undefined behavior manifests itself can change if you use a different compiler or different optimization settings on the same compiler.

    The C standard states the following regarding the bitwise shift operators in section 6.5.7p3:

    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.

    In this case it's possible that the compiler could reduce the amount to shift modulo the bit width, as you suggested, or it could treat it as mathematically shifting by that amount resulting in all bits being 0. Either is a valid result because the standard does not specify the behavior.