为什么 1ul << 64 return 1 而不是 0?
Why does 1ul << 64 return 1 instead of 0?
考虑以下代码:
// Simply loop over until 64 is hit.
unsigned long x = 0;
for (int i = 0; i <= 64; i++) {
if (i == 64) {
x = 1ul << i;
printf("x: %d\n", x);
}
}
我们知道 unsigned long 是 64 位宽,左移 1 64 位会变成 1000...000(1 后面有 64 个零),并且会被截断为 0。但是,实际打印输出给出:
x: 1
奇怪的是,如果我们只是
printf("x: %d\n", (1ul << 64));
它会打印 0。
谁能解释为什么会这样?为什么在第一种情况下,程序错误地产生了 1 而不是 0,而在第二种情况下它是正确的?
根据 §6.5.7/3,按一个或多个类型的宽度移动会导致 undefined behaviour:
- The integer promotions are performed on each of the operands. The type
of the result is that of the promoted left operand. If the value of
the right operand is negative or is greater than or equal to the width
of the promoted left operand, the behavior is undefined.
这样做的理由是,各种 CPU 对超大班次实施不同的行为,并且定义这种情况下的行为过于严格——许多班次需要生成额外的程序集。 (尽管也许它应该是实现定义的而不是未定义的)。
您的观察可以通过使用 Intel 家族 CPU 来解释,它在硬件中对 64 位类型的移位宽度 "mod 64",因此在运行时 1ul << 64
也是如此正如 1ul << 0
那样;但在另一种情况下,编译器在编译时使用算术规则计算 1ul << 64
。
考虑以下代码:
// Simply loop over until 64 is hit.
unsigned long x = 0;
for (int i = 0; i <= 64; i++) {
if (i == 64) {
x = 1ul << i;
printf("x: %d\n", x);
}
}
我们知道 unsigned long 是 64 位宽,左移 1 64 位会变成 1000...000(1 后面有 64 个零),并且会被截断为 0。但是,实际打印输出给出:
x: 1
奇怪的是,如果我们只是
printf("x: %d\n", (1ul << 64));
它会打印 0。
谁能解释为什么会这样?为什么在第一种情况下,程序错误地产生了 1 而不是 0,而在第二种情况下它是正确的?
根据 §6.5.7/3,按一个或多个类型的宽度移动会导致 undefined behaviour:
- The integer promotions are performed on each of the operands. The type of the result is that of the promoted left operand. If the value of the right operand is negative or is greater than or equal to the width of the promoted left operand, the behavior is undefined.
这样做的理由是,各种 CPU 对超大班次实施不同的行为,并且定义这种情况下的行为过于严格——许多班次需要生成额外的程序集。 (尽管也许它应该是实现定义的而不是未定义的)。
您的观察可以通过使用 Intel 家族 CPU 来解释,它在硬件中对 64 位类型的移位宽度 "mod 64",因此在运行时 1ul << 64
也是如此正如 1ul << 0
那样;但在另一种情况下,编译器在编译时使用算术规则计算 1ul << 64
。