Search code examples
c++bit-manipulationbitwise-operators

Sign extending bit shifted binary values in C++ using only bitwise operators


I have written the following function which is meant to extract bits 5 through 8 (inclusive) of an integer and return those bits. However, the extracted bits are treated as signed so therefore they must be sign extended after bit shifting.

int signedBits5through8(int v){
  int fourBits = (v & 0b111111111) >> 5;
  int signBit = (fourBits & 0b1000) >> 3;
  return (0b11111111111111111111111111110000 * signBit) | fourBits;
}

In order to achieve sign extension, my implementation multiplies the sign bit (the most significant bit of the extracted 4 bits, following twos complement conventions) by 28 bits of 1s followed by 4 bits of zeros in order to "sign extend", and then uses an OR mask to apply the last 4 bits. My issue is that I am unable to use any operators other than bitwise ones for this task, and so using the multiplication operator is unacceptable. What's the best work-around in this situation? I also cannot use any conditional or loop logic (if-else, for, etc) in my function.


Solution

  • As @user207421 suggested in the comments, you can sign-extend by bit-shifting without using the sign bit explicitly.

    Note that before when two's complement representation was not yet required by the standard, this was considered conditionally UB (even though MSVC seems not to think so, which is probably a compiler bug) if the left shift causes signed overflow.

    As code, the suggestion looks like this:

    int signedBits5through8(int v) {
      return (v << 23) >> 28;
    }
    

    Here are some tests to show it works as intended in all the major compilers, using constant evaluation to ensure that no UB is evaluated (at least with standard enabled): https://godbolt.org/z/6aonbxsbs

    constexpr int signedBits5through8(int v) {
      return (v << 23) >> 28;
    }
    
    static_assert(signedBits5through8(0b11111111111111111111111'0000'11111) == +0b0000);
    static_assert(signedBits5through8(0b11111111111111111111111'0001'11111) == +0b0001);
    static_assert(signedBits5through8(0b11111111111111111111111'0010'11111) == +0b0010);
    static_assert(signedBits5through8(0b11111111111111111111111'0011'11111) == +0b0011);
    static_assert(signedBits5through8(0b11111111111111111111111'0100'11111) == +0b0100);
    static_assert(signedBits5through8(0b11111111111111111111111'0101'11111) == +0b0101);
    static_assert(signedBits5through8(0b11111111111111111111111'0110'11111) == +0b0110);
    static_assert(signedBits5through8(0b11111111111111111111111'0111'11111) == +0b0111);
    static_assert(signedBits5through8(0b11111111111111111111111'1000'11111) == -0b1000);
    static_assert(signedBits5through8(0b11111111111111111111111'1001'11111) == -0b0111);
    static_assert(signedBits5through8(0b11111111111111111111111'1010'11111) == -0b0110);
    static_assert(signedBits5through8(0b11111111111111111111111'1011'11111) == -0b0101);
    static_assert(signedBits5through8(0b11111111111111111111111'1100'11111) == -0b0100);
    static_assert(signedBits5through8(0b11111111111111111111111'1101'11111) == -0b0011);
    static_assert(signedBits5through8(0b11111111111111111111111'1110'11111) == -0b0010);
    static_assert(signedBits5through8(0b11111111111111111111111'1111'11111) == -0b0001);