2

I am doing the following bitwise shift in Microsoft C++:

uint8_t arr[3] = {255, 255, 255};
uint8_t value = (arr[1] << 4) >> 4;

The result of these operations confused me quite a bit:

value = 255

However, if I do the bitwise shift separately:

value = (arr[i] << 4);
value = value >> 4;

the answer is different and makes much sense:

value = 15

Can someone explain to me why this happens? I am familiar with the concepts of bitwise shift, or so I believed...

Thanks in advance!

(P.S.: It seems g++ will have the same behavior. I am probably missing some important concepts with bitwise shift. Any help is greatly appreciated!)

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • duplicates: [Unexepected behavior from multiple bitwise shifts on the same line](https://stackoverflow.com/q/25393058/995714), [Why does combining two shifts of a uint8_t produce a different result?](https://stackoverflow.com/q/22702091/995714), [Why does it make a difference if left and right shift are used together in one expression or not?](https://stackoverflow.com/q/61958789/995714) – phuclv Jan 20 '22 at 09:42
  • Yeah, it does! I really wish I could find that post earlier.. LOL – Runsheng Ma Jan 20 '22 at 09:43

2 Answers2

2

In this expression with shift operators

(arr[1] << 4) >> 4;

there is used the integral promotions. That is the operand arr[1] is promoted to an object of the type int and such an object can store the result of the expression arr[i] << 4.

From the C++ 14 Standard (5.8 Shift operators, p.#1)

...The operands shall be of integral or unscoped enumeration type and integral promotions are performed. The type of the result is that of the promoted left operand. The behavior is undefined if the right operand is negative, or greater than or equal to the length in bits of the promoted left operand.

Here is a demonstration program

#include <iostream>
#include <iomanip>
#include <type_traits>
#include <cstdint>

int main()
{
    uint8_t x = 255;

    std::cout << "std::is_same_v<decltype( x << 4 ), int> is "
              << std::boolalpha
              << std::is_same_v<decltype( x << 4 ), int> << '\n';

    std::cout << "x << 4 = " << ( x << 4 ) << '\n';
}

The program output is

std::is_same_v<decltype( x << 4 ), int> is true
x << 4 = 4080

As for this code snippet

value = (arr[i] << 4);
value = value >> 4;

then in the first assignment statement the result of the shift operation is truncated.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • Thanks for your answer as well! Your explanation and the marked one are equally good, and I really wish I could mark both solutions. – Runsheng Ma Jan 20 '22 at 09:47
1

Expression (arr[1] << 4) will implicitly promote the value of arr[1] to type unsigned int before applying the shift operation, such that the "intermediate" result will not "loose" any bits (cf, for example, the explanation in implicit conversions).

However, when you write value = (arr[i] << 4);, then this "intermediate" result will be converted back to uint_8, and in this step bits get cut off.

See the difference when you write uint8_t value = ((uint8_t)(arr[1] << 4)) >> 4;

Alan Birtles
  • 32,622
  • 4
  • 31
  • 60
Stephan Lechner
  • 34,891
  • 4
  • 35
  • 58