简体   繁体   中英

bit shift - shifting a shifted value

I have tried to make a longer function but it has acted very weird. I had tried to isolate the problem and I have been able to find the buggy part.

This program made for an Arduino but this phenomenon probably appear in other environment. I have tried to make a lot of searches but I cannot find the solution.

So, my buggy part:

Why do not these two codes give the same result?
How can I construct a one line function without extra variable
but same operation like "Code 1"?

Results:

  • Code 1: 0b00101100
  • Code 2: 0b01101100

Source codes:


Code 1: (correct operation but not one line)

#include <binary.h>

const byte value=B00110110;
byte buffer,result;

void setup(){
  Serial.begin(115200);

  buffer = (value << 3);
  result = (buffer >> 2);

  Serial.println(result,BIN);
}

void loop(){
}

It gives: 0b00101100


Code 2: (incorrect operation but one line)

#include <binary.h>

const byte value=B00110110;
byte result;

void setup(){
  Serial.begin(115200);

  result = ((value << 3) >> 2);

  Serial.println(result,BIN);
}

void loop(){
}

It gives: 0b01101100


Remove the extra bits by masking with 0xFF :

result = (((value << 3) & 0xFF) >> 2);

You can also trim the upper three bits off after the sequence of shifts:

result = 0x1F & ((value << 3) >> 2);

According to the language specification, default integer promotions are applied to shift operands before shifting, and the result of the operation is the promoted type of the first operand.

5.8.1 The operands shall be of integral or enumerated type and integral promotions are performed. The type of the result is that of the promoted left operand.

If an integral type can fit in an int , then int is the result of the promotion. When you shift left, the most significant bits "spill" into the upper part of the int . Your first snippet cuts them off by assigning back to byte ; you can achieve the same result by masking the result with 0xFF .

Link to ideone .

If byte is a uint8_t (thanks Mr. Fernandes!), the result of (value << 3) is wider than a byte . More precisely, value is promoted to a wider type before the shift occurs, and the result is that wider type. To get the behavior of Code 1, you can change Code 2 to do this:

result = (static_cast<byte>(value << 3) >> 2);

This mimics the intermediate assignment to buffer in Code 1 with a cast.

I assume that your byte is some unsigned integral type smaller than int .

Most arithmetic operations in C++ are performed in the domain of int type, after all "smaller" operands are implicitly converted ( promoted ) to type int .

For this reason, your first group of shifts is equivalent to

buffer = (byte) ((int) value << 3);
result = (byte) ((int) buffer >> 2);

while your second group if shifts is equivalent to

result = (byte) (((int) value << 3) >> 2);

In the first variant, any bits that get shifted outside the range of type byte by the << 3 shift are lost during intermediate conversion and assignment to buffer . In the second variant all calculations are performed within the range of int , so nothing is lost.

If you want your second variant to produce the same results as the first, you need to explicitly "truncate" the intermediate shift result

result = ((byte) (value << 3) >> 2);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM