为什么printf和cout为这段代码提供不同的输出?

时间:2013-10-25 14:43:21

标签: c++ bit-shift

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表现不同?这种转变究竟是如何运作的?

8 个答案:

答案 0 :(得分:4)

printf格式标志%x表示以十六进制打印整数值。

还有一个流操作器来实现这一点(std::hex),但你没有使用它。当您向没有操纵器的流输出积分值时,它将以基数10输出。

See here有关printf格式标志的更多信息,and here有关流操作符的信息。

移位运算符<<的工作原理如C ++ 03标准(14882:2003)所述:

5.8移位运算符

  

1 /移位运算符&lt;&lt;和&gt;&gt;从左到右组。       移位表达:           添加剂的表达           shift-expression&lt;&lt;添加剂的表达           shift-expression&gt;&gt;添加剂的表达

     

操作数应为整数或枚举类型和整数   促销活动。结果的类型是   提升左操作数。如果右操作数,则行为未定义   是负的,或大于或等于的长度   提升左操作数。

     

2 / E1的值<&lt; E2是E1(解释为位模式)   左移E2位位置;空位是零填充的。如果E1有   无符号类型,结果的值是E1乘以   数量2增加到功率E2,减少模数ULONG_MAX + 1,如果E1   类型为unsigned long,否则为UINT_MAX + 1。

     

[注意:标题中定义了常量ULONG_MAX和UINT_MAX   )。 ]

在您的情况下,二进制中的值-1在每个位中都是1。对于32位值,则:

11111111 11111111 11111111 11111111 

如果使用<<将此位向左移1位,则会得到:

11111111 11111111 11111111 11111110 

在10的基数是-2。由于操作-1<<1使用LHS的负数,因此整个表达式是带符号(非符号)类型。

答案 1 :(得分:1)

首先,

  

十六进制的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<<重载使用的强类型,您遇到了溢出问题。

答案 2 :(得分:1)

此代码调用undefined behavior,因为您尝试左移为负数,the draft C++11 Standard部分5.8 Shift运算符表示(强调我的):

  

E1的值<&lt; E2是E1左移E2位位置;腾空   位是零填充的。如果E1具有无符号类型,则为   结果是E1×2E2,比最大值减少一个模数   在结果类型中可表示。否则,如果 E1具有签名类型   和非负值,E1×2E2可在结果中表示   type,那就是结果值;否则,行为是   未定义

对于draft C99 standard部分6.5.7 按位移位运算符

,这也是相同的

未定义的行为指定无效的转换说明符到printf,您指定的%x期望无符号int 但结果是签名7.19.6.1 部分中的 C99草案 fprintf函数 9 表示:

  

如果转换规范无效,则行为未定义.248)如果任何参数不是相应转换规范的正确类型,则行为为   未定义。

答案 3 :(得分:0)

%x格式化输出以十六进制显示值,因此您应该将std::hex传递给cout以执行相同的操作:

std::cout << std:::hex << (-1<<1) << endl;
             ^^^^^^^^^

答案 4 :(得分:0)

这将产生正确的相同答案,因为您需要告诉它以十六进制打印

cout << hex<<(-1<<1)<<endl;
printf("%x",-1<<1);

答案 5 :(得分:0)

%x是十六进制,因为cout只是直接输出数字(int) -1是0xFFFFFFFF,向左移动你必须加0 即所以最后的F(二进制是1111变为1110即E 其他人更清楚地回答你的观点我想......

答案 6 :(得分:0)

为了便于阅读,我们使用带符号的8位整数:

-1 bitwise is 11111111

现在左移1:

-1 << 1

你得到:

11111110 which is -2

但是,使用转化说明符%x会让printf()11111111作为取消 - 签名,以便打印出fe

答案 7 :(得分:0)

第一种情况有效:pow(2,32)返回精确值2 32 作为double,当您将其转换为long long时,它仍然是准确的(I {I}}我假设这就是神秘的ll类型。使用cout打印此内容非常有效。

(-1<<1)的情况下,左移一个负数会产生未定义的行为。即使编译器确实将其定义为-2(大多数情况下),它也是未定义的行为,用于传递负数以便与printf的{​​{1}}说明符一起使用(需要无符号类型)。