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

4

8 回答 8

4

printf格式标志意味着以%x十六进制打印整数值。

也有一个流操纵器来完成此操作 ( std::hex),但您没有使用它。当您将整数值输出到没有操纵器的流时,它以 10 为底输出。

printf有关格式标志的更多信息,请参见此处有关流操纵器的信息,请参见此处。

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

5.8 移位运算符

1/ 移位运算符 << 和 >> 从左到右分组。移位表达式:加法表达式移位表达式<<加法表达式移位表达式>>加法表达式

操作数应为整数或枚举类型,并执行整数提升。结果的类型是提升的左操作数的类型。如果右操作数为负数,或者大于或等于提升的左操作数的位长度,则行为未定义。

2/ E1 << E2 的值是 E1(解释为位模式)左移 E2 位位置;空出的位用零填充。如果 E1 具有无符号类型,则结果的值是 E1 乘以 2 的 E2 次幂,如果 E1 具有无符号长类型,则以模 ULONG_MAX+1 减少,否则为 UINT_MAX+1。

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

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

11111111 11111111 11111111 11111111 

如果使用 将此左移 1 位<<,您将得到:

11111111 11111111 11111111 11111110 

以 10 为底的是 -2。由于该运算-1<<1对 LHS 使用负数,因此整个表达式是有符号(非无符号)类型。

于 2013-10-25T14:48:14.150 回答
1

第一的,

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

于 2013-10-25T14:51:20.253 回答
1

此代码调用未定义的行为,因为您试图左移一个负数,草案 C++11 标准 部分5.8 Shift 运算符说(强调我的):

E1 << E2 的值是 E1 左移 E2 位位置;空出的位用零填充。如果 E1 具有无符号类型,则结果的值为 E1 × 2E2,比结果类型中可表示的最大值多模一减少。否则,如果E1 具有带符号类型和非负值,并且 E1×2E2 在结果类型中是可表示的,那么这就是结果值;否则,行为是 undefined

这对于草案 C99 标准部分按位移位运算符也是相同的6.5.7

无效的转换说明符指定为 也是未定义的行为,您正在指定哪个期望unsigned int但结果是有符号的。The fprintf function9节中的C99 草案说:printf%x7.19.6.1

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

于 2013-10-25T15:03:45.327 回答
0

%x格式化输出以显示十六进制的值,所以你应该传递std::hexcout做同样的事情:

std::cout << std:::hex << (-1<<1) << endl;
             ^^^^^^^^^
于 2013-10-25T14:46:26.247 回答
0

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

cout << hex<<(-1<<1)<<endl;
printf("%x",-1<<1);
于 2013-10-25T14:48:54.810 回答
0

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

于 2013-10-25T14:49:03.220 回答
0

为了便于阅读,让我们使用一个有符号的 8 位整数:

-1 bitwise is 11111111

现在左移 1:

-1 << 1

你得到:

11111110 which is -2

但是,使用转换说明符会%x告诉printf()您以un11111111 -signed方式运行,因此它会打印出来。fe

于 2013-10-25T14:56:59.170 回答
0

第一种情况是有效pow(2,32)的:返回精确值 2 32作为 a double,当您将其转换为时保持精确long long(我假设这就是神秘ll类型)。打印这个cout是完全有效的。

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

于 2013-10-25T15:01:49.383 回答