Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why result of unsigned char << unsigned char is not unsigned char

I'm getting results from left shift to which I could not find an explanation.

unsigned char value = 0xff; // 1111 1111
unsigned char  = 0x01; // 0000 0001

std::cout << "SIZEOF value " << sizeof(value) << "\n"; // prints 1 as expected
std::cout << "SIZEOF shift " << sizeof(shift) << "\n"; // prints 1 as expected

std::cout << "result " << (value << shift) << "\n"; // prints 510 ???

std::cout << "SIZEOF result " <<  sizeof(value << shift) << "\n"; // prints 4 ???

I was expecting result to be 1111 1110 but instead I get int(?) with value of 1 1111 1110.

How can the bits of an unsigned char be shifted to the left so that bits are truncated and the result is 1111 1110?

What I'm trying to do is to read series of bytes and interpret them as integers of varying lengths (1-32 bits).

F0        F5
1111 0000 1111 0101 

could be

0F (first 4 bits)
0F (next 8 bits)
05 (last 4 bits)

Has this something to do with the fact that arithmetic is not done with types smaller than int?

like image 301
user1124809 Avatar asked Jan 03 '12 14:01

user1124809


3 Answers

Quoting some draft of the 2011 standard:

5.8 Shift operators [expr.shift]

...

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.

and

4.5 Integral Promotions [conv.prom]

A prvalue of an integer type other than bool, char16_t, char32_t, or wchar_t whose integer conversion rank (4.13) is less than the rank of int can be converted to a prvalue of type int if int can represent all the values of the source type; otherwise, the source prvalue can be converted to a prvalue of type unsigned int

...

So, value is promoted to int, and the type of value << shift is the type of the promoted left operand, i.e. int.

You can achieve your desired result one of these ways:

std::cout << "result " << ((unsigned char)(value << shift)) << "\n";
std::cout << "result " << ((value << shift)&0xff) << "\n";
like image 200
Robᵩ Avatar answered Nov 18 '22 16:11

Robᵩ


Just cast it back to an unsigned char:

std::cout << "result " << static_cast<unsigned char>(value << shift) << "\n";

Or, use bitwise-AND:

std::cout << "result " << ((value << shift) & 0xFF) << "\n";
like image 2
James Johnston Avatar answered Nov 18 '22 16:11

James Johnston


You could mask the bits you're interested in:

(value << shift) & 0xff

What you are seeing is a result of integer promotion, and is a part of the language. Imagine you're composing a 16-bit integer from 2 8-bit ones - you wouldn't want to manually promote to a higher width integer to get the high and low bits into the right place, right?

like image 2
Tom Whittock Avatar answered Nov 18 '22 16:11

Tom Whittock