I have a simple program. Notice that I use an unsigned fixed-width integer 1 byte in size.
#include <cstdint>
#include <iostream>
#include <limits>
int main()
{
uint8_t x = 12;
std::cout << (x << 1) << '\n';
std::cout << ~x;
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
std::cin.get();
return 0;
}
My output is the following.
24
-13
I tested larger numbers and operator <<
always gives me positive numbers, while operator ~
always gives me negative numbers. I then used sizeof()
and found...
When I use the left shift bitwise operator(
<<
), I receive an unsigned 4 byte integer.When I use the bitwise not operator(
~
), I receive a signed 4 byte integer.
It seems that the bitwise not operator(~
) does a signed integral promotion like the arithmetic operators do. However, the left shift operator(<<
) seems to promote to an unsigned integral.
I feel obligated to know when the compiler is changing something behind my back. If I'm correct in my analysis, do all the bitwise operators promote to a 4 byte integer? And why are some signed and some unsigned? I'm so confused!
Edit: My assumption of always getting positive or always getting negative values was wrong. But from being wrong, I understand what was really happening thanks to the great answers below.
[expr.unary.op]
The operand of
~
shall have integral or unscoped enumeration type; the result is the one’s complement of its operand. Integral promotions are performed.
[expr.shift]
The shift operators
<<
and>>
group left-to-right. [...] The operands shall be of integral or unscoped enumeration type and integral promotions are performed.
What's the integral promotion of uint8_t
(which is usually going to be unsigned_char
behind the scenes)?
[conv.prom]
A prvalue of an integer type other than
bool
,char16_t
,char32_t
, orwchar_t
whose integer conversion rank (4.13) is less than the rank ofint
can be converted to a prvalue of typeint
ifint
can represent all the values of the source type; otherwise, the source prvalue can be converted to a prvalue of typeunsigned int
.
So int
, because all of the values of a uint8_t
can be represented by int
.
What is int(12) << 1
? int(24)
.
What is ~int(12)
? int(-13)
.