[英]Why does printf and cout gives different output for this code?
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 Standard(14882:2003)中进行了描述:
1 /移位运算符<<和>>从左到右分组。 shift-expression:加法表达式加法表达式<<加法表达式加法表达式>>加法表达式
操作数应为整数或枚举类型,并执行整数提升。 结果的类型是提升后的左操作数的类型。 如果右操作数为负或大于或等于提升后的左操作数的位长度,则该行为是不确定的。
2 / E1 << E2的值是E1(被解释为位模式)左移的E2位位置; 空出的位为零。 如果E1具有无符号类型,则将结果的值乘以E1乘以幂2的幂2,如果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", ...)
将输出一个无符号的十六进制整数(即unsigned int
),在大多数现代系统上,该整数为32位。 long long a = 2 << 32
需要64位整数才能正确存储它(或更准确地说,至少需要33位整数),因此,当您使用cout << a
,您正在调用ostream& operator<<(ostream&, long long)
,其类型正确。 也就是说,由于printf说明符使用的类型与C ++ operator<<
重载使用的强类型,因此您遇到了溢出问题。
该代码调用了未定义的行为,因为您正尝试左移负数, C ++ 11标准草案 5.8
移位运算符说( 强调我的意思 ):
E1 << E2的值是E1左移E2位的位置; 空出的位为零。 如果E1具有无符号类型,则结果的值为E1×2E2,以比结果类型中可表示的最大值大的模数减少1。 否则,如果E1具有带符号类型且非负值 ,并且E1×2E2在结果类型中可表示,则这是结果值;否则,结果为0。 否则, 行为是undefined 。
草案C99标准第6.5.7
节的按位移位运算符也是如此
为printf
指定无效的转换说明符也是未定义的行为 ,您指定%x
期望有一个无符号的int但结果是带符号的 。 第7.19.6.1
节中的C99草案 fprintf函数第9段说:
如果转换规范无效,则行为是不确定的。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()
将11111111
设为无符号,以便打印出fe
。
第一种情况是有效的: pow(2,32)
返回精确值2 32作为double
,当您将其转换为long long
时,它仍然是精确值(我假设这就是神秘的ll
类型)。 使用cout
打印此文件是完全有效的。
在(-1<<1)
的情况下,左移负数会产生不确定的行为。 即使编译器确实将其定义为-2(大多数情况下也是如此),传递负数以与printf
的%x
说明符(要求使用无符号类型)一起使用也是一种不确定的行为。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.