按位运算符和整数提升是怎么回事?
What is going on with bitwise operators and integer promotion?
我有一个简单的程序。 请注意,我使用了一个大小为 1 字节的无符号固定宽度整数。
#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;
}
我的输出如下。
24
-13
我测试了更大的数字,运算符 <<
总是给我正数,而运算符 ~
总是给我负数。然后我使用 sizeof()
并发现...
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.
按位非运算符 (~
) 似乎像算术运算符一样进行有符号整数提升。但是,左移运算符(<<
)似乎提升为无符号积分。
我觉得有义务知道编译器何时在我背后更改某些内容。如果我的分析是正确的,那么所有按位运算符是否都提升为 4 字节整数?为什么有的有签名有的没有签名?我很困惑!
编辑: 我总是得到正值或总是得到负值的假设是错误的。但是从错误中,由于下面的伟大答案,我明白了真正发生的事情。
研究 two's complement 以及计算机如何存储负整数。
试试这个
#include <cstdint>
#include <iostream>
#include <limits>
int main()
{
uint8_t x = 1;
int shiftby=0;
shiftby=8*sizeof(int)-1;
std::cout << (x << shiftby) << '\n'; // or std::cout << (x << 31) << '\n';
std::cout << ~x;
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
std::cin.get();
return 0;
}
输出为-2147483648
一般来说,如果有符号数的第一位是 1,则被认为是负数。当你拿一个大数字并转移它时。如果你移动它使得第一位是 1 它将是负数
** 编辑 **
好吧,我能想到移位运算符使用 unsigned int 的原因。考虑右移操作 >>
如果你右移 -12 你会得到 122 而不是 -6。这是因为它在开头添加了一个零而没有考虑符号
[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.
uint8_t
的积分推广是什么(一般都是unsigned_char
在幕后)?
[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
.
所以int
,因为一个uint8_t
的所有值都可以用int
表示。
什么是int(12) << 1
? int(24)
.
什么是~int(12)
? int(-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...
错了,测试一下:
uint8_t v = 1;
for (int i=0; i<32; i++) cout << (v<<i) << endl;
给出:
1
2
4
8
16
32
64
128
256
512
1024
2048
4096
8192
16384
32768
65536
131072
262144
524288
1048576
2097152
4194304
8388608
16777216
33554432
67108864
134217728
268435456
536870912
1073741824
-2147483648
uint8_t
是一个8位长无符号整数类型,可以表示[0,255]范围内的值,因为该范围包含在int
的范围内,所以提升为int
(不是 unsigned int
)。升级到 int
优先于升级到 unsigned
。
出于性能原因,C 和 C++ 语言将 int
视为 "most natural" 整数类型,而不是 "smaller" 而不是 int
的类型被认为是某种类型"storage"类型。
当您在表达式中使用存储类型时,它会自动转换为 int
或隐式转换为 unsigned int
。例如:
// Assume a char is 8 bit
unsigned char x = 255;
unsigned char one = 1;
int y = x + one; // result will be 256 (too large for a byte!)
++x; // x is now 0
发生的事情是第一个表达式中的 x
和 one
已隐式转换为整数,已计算加法并将结果存储回整数。换句话说,计算尚未使用两个无符号字符执行。
同样,如果表达式中有一个 float
值,编译器要做的第一件事就是将其提升为 double
(换句话说,float
是一种存储类型,并且double
是浮点数的自然大小)。这就是为什么如果你使用 printf
来打印浮点数你不需要说 %lf
int 格式字符串并且 %f
就足够了(%lf
需要scanf
但是因为该函数 存储结果 而 float
可以小于 double
).
C++ 使事情变得相当复杂,因为在将参数传递给函数时,您可以区分 int
和更小的类型。因此,在每个表达式中执行转换并不总是正确的......例如你可以有:
void foo(unsigned char x);
void foo(int x);
哪里
unsigned char x = 255, one = 1;
foo(x); // Calls foo(unsigned char), no promotion
foo(x + one); // Calls foo(int), promotion of both x and one to int
我有一个简单的程序。 请注意,我使用了一个大小为 1 字节的无符号固定宽度整数。
#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;
}
我的输出如下。
24
-13
我测试了更大的数字,运算符 <<
总是给我正数,而运算符 ~
总是给我负数。然后我使用 sizeof()
并发现...
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.
按位非运算符 (~
) 似乎像算术运算符一样进行有符号整数提升。但是,左移运算符(<<
)似乎提升为无符号积分。
我觉得有义务知道编译器何时在我背后更改某些内容。如果我的分析是正确的,那么所有按位运算符是否都提升为 4 字节整数?为什么有的有签名有的没有签名?我很困惑!
编辑: 我总是得到正值或总是得到负值的假设是错误的。但是从错误中,由于下面的伟大答案,我明白了真正发生的事情。
研究 two's complement 以及计算机如何存储负整数。
试试这个
#include <cstdint>
#include <iostream>
#include <limits>
int main()
{
uint8_t x = 1;
int shiftby=0;
shiftby=8*sizeof(int)-1;
std::cout << (x << shiftby) << '\n'; // or std::cout << (x << 31) << '\n';
std::cout << ~x;
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
std::cin.get();
return 0;
}
输出为-2147483648
一般来说,如果有符号数的第一位是 1,则被认为是负数。当你拿一个大数字并转移它时。如果你移动它使得第一位是 1 它将是负数
** 编辑 **
好吧,我能想到移位运算符使用 unsigned int 的原因。考虑右移操作 >>
如果你右移 -12 你会得到 122 而不是 -6。这是因为它在开头添加了一个零而没有考虑符号
[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.
uint8_t
的积分推广是什么(一般都是unsigned_char
在幕后)?
[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
.
所以int
,因为一个uint8_t
的所有值都可以用int
表示。
什么是int(12) << 1
? int(24)
.
什么是~int(12)
? int(-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...
错了,测试一下:
uint8_t v = 1;
for (int i=0; i<32; i++) cout << (v<<i) << endl;
给出:
1
2
4
8
16
32
64
128
256
512
1024
2048
4096
8192
16384
32768
65536
131072
262144
524288
1048576
2097152
4194304
8388608
16777216
33554432
67108864
134217728
268435456
536870912
1073741824
-2147483648
uint8_t
是一个8位长无符号整数类型,可以表示[0,255]范围内的值,因为该范围包含在int
的范围内,所以提升为int
(不是 unsigned int
)。升级到 int
优先于升级到 unsigned
。
出于性能原因,C 和 C++ 语言将 int
视为 "most natural" 整数类型,而不是 "smaller" 而不是 int
的类型被认为是某种类型"storage"类型。
当您在表达式中使用存储类型时,它会自动转换为 int
或隐式转换为 unsigned int
。例如:
// Assume a char is 8 bit
unsigned char x = 255;
unsigned char one = 1;
int y = x + one; // result will be 256 (too large for a byte!)
++x; // x is now 0
发生的事情是第一个表达式中的 x
和 one
已隐式转换为整数,已计算加法并将结果存储回整数。换句话说,计算尚未使用两个无符号字符执行。
同样,如果表达式中有一个 float
值,编译器要做的第一件事就是将其提升为 double
(换句话说,float
是一种存储类型,并且double
是浮点数的自然大小)。这就是为什么如果你使用 printf
来打印浮点数你不需要说 %lf
int 格式字符串并且 %f
就足够了(%lf
需要scanf
但是因为该函数 存储结果 而 float
可以小于 double
).
C++ 使事情变得相当复杂,因为在将参数传递给函数时,您可以区分 int
和更小的类型。因此,在每个表达式中执行转换并不总是正确的......例如你可以有:
void foo(unsigned char x);
void foo(int x);
哪里
unsigned char x = 255, one = 1;
foo(x); // Calls foo(unsigned char), no promotion
foo(x + one); // Calls foo(int), promotion of both x and one to int