int main(){
ll a=pow(2,32);
cout <<a<<endl;
cout << (-1<<1)<<endl;
printf("%x",-1<<1);
}
对于上面的代码,我得到以下输出:
4294967296
-2
fffffffe
4294967296
十进制等于fffffffe
十六进制,基本上是2^32
. 为什么 printf 和 cout 的行为不同?这种转变究竟是如何运作的?
printf
格式标志意味着以%x
十六进制打印整数值。
也有一个流操纵器来完成此操作 ( std::hex
),但您没有使用它。当您将整数值输出到没有操纵器的流时,它以 10 为底输出。
printf
有关格式标志的更多信息,请参见此处,有关流操纵器的信息,请参见此处。
移位运算符的<<
工作方式如 C++03 标准 (14882:2003) 中所述:
1/ 移位运算符 << 和 >> 从左到右分组。移位表达式:加法表达式移位表达式<<加法表达式移位表达式>>加法表达式
操作数应为整数或枚举类型,并执行整数提升。结果的类型是提升的左操作数的类型。如果右操作数为负数,或者大于或等于提升的左操作数的位长度,则行为未定义。
2/ E1 << E2 的值是 E1(解释为位模式)左移 E2 位位置;空出的位用零填充。如果 E1 具有无符号类型,则结果的值是 E1 乘以 2 的 E2 次幂,如果 E1 具有无符号长类型,则以模 ULONG_MAX+1 减少,否则为 UINT_MAX+1。
[注意:常量 ULONG_MAX 和 UINT_MAX 在标题中定义)。]
在您的情况下,-1
二进制中的值在每一位都是1
s 。对于 32 位值,则:
11111111 11111111 11111111 11111111
如果使用 将此左移 1 位<<
,您将得到:
11111111 11111111 11111111 11111110
以 10 为底的是 -2。由于该运算-1<<1
对 LHS 使用负数,因此整个表达式是有符号(非无符号)类型。
第一的,
fffffffe 十六进制,基本上是 2^32
是错的。FFFFFFFE = 4294967294,即 2^32 - 2(对于无符号整数)或 -2(对于 2 的补码中的 32 位有符号整数)。
其次,printf("%x", ...)
将打印一个无符号的十六进制整数(即 a unsigned int
),在大多数现代系统上它是 32 位的。 long long a = 2 << 32
需要一个 64 位整数来正确存储它(或者更准确地说,至少是一个 33 位整数),所以当您使用 时cout << a
,您正在调用ostream& operator<<(ostream&, long long)
具有正确类型的 。也就是说,由于 printf 说明符使用的类型与 C++operator<<
重载使用的强类型相比,您遇到了溢出问题。
此代码调用未定义的行为,因为您试图左移一个负数,草案 C++11 标准 部分5.8
Shift 运算符说(强调我的):
E1 << E2 的值是 E1 左移 E2 位位置;空出的位用零填充。如果 E1 具有无符号类型,则结果的值为 E1 × 2E2,比结果类型中可表示的最大值多模一减少。否则,如果E1 具有带符号类型和非负值,并且 E1×2E2 在结果类型中是可表示的,那么这就是结果值;否则,行为是 undefined。
这对于草案 C99 标准部分按位移位运算符也是相同的6.5.7
将无效的转换说明符指定为 也是未定义的行为,您正在指定哪个期望unsigned int但结果是有符号的。The fprintf function第9节中的C99 草案说:printf
%x
7.19.6.1
如果转换规范无效,则行为未定义。248) 如果任何参数不是相应转换规范的正确类型,则行为未定义。
%x
格式化输出以显示十六进制的值,所以你应该传递std::hex
给cout
做同样的事情:
std::cout << std:::hex << (-1<<1) << endl;
^^^^^^^^^
这将产生正确的答案,因为您需要告诉它以十六进制打印
cout << hex<<(-1<<1)<<endl;
printf("%x",-1<<1);
%x 是十六进制,因为 cout 只是直接输出数字(int) -1 是 0xFFFFFFFF 向左移动你必须添加一个 0 即最后一个 F (二进制是 1111 变成 1110 即 E 其他人我认为更清楚地回答你的观点....
为了便于阅读,让我们使用一个有符号的 8 位整数:
-1 bitwise is 11111111
现在左移 1:
-1 << 1
你得到:
11111110 which is -2
但是,使用转换说明符会%x
告诉printf()
您以un11111111
-signed方式运行,因此它会打印出来。fe
第一种情况是有效pow(2,32)
的:返回精确值 2 32作为 a double
,当您将其转换为时保持精确long long
(我假设这就是神秘ll
类型)。打印这个cout
是完全有效的。
在 的情况下(-1<<1)
,左移一个负数会产生未定义的行为。即使编译器确实将其定义为 -2(大多数情况下),传递一个负数以用于printf
's%x
说明符(需要无符号类型)也是未定义的行为。