为什么我需要从1<<<< 64得到2 ^ 64 - 1?

时间:2013-08-30 01:11:44

标签: c

该程序输出的每一行等于2 ^ i - 2,除了最后一行,等于2 ^ 64 - 1.为什么会这样?

#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>

int main(void) {
    unsigned long long ONE = 1;
    unsigned long long i;
    for (i = 1; i <= 64; i++) {
        printf("%"PRIu64"\n", (ONE << i) - 2);
    }

    return EXIT_SUCCESS;
}

输出:

0
2
6
14
30
62
126
254
510
1022
2046
...
4611686018427387902
9223372036854775806
18446744073709551615

4 个答案:

答案 0 :(得分:5)

您将64位移到64位类型(计算机上为unsigned long long),这是未定义的行为。

BTW,unsigned long long ONE = 1;编码风格不好,您只需使用1ULL

  

C11§6.5.7按位移位运算符

     

对每个操作数执行整数提升。结果的类型是   升级的左操作数。 如果右操作数的值为负或大于或等于提升的左操作数的宽度,则行为未定义。

答案 1 :(得分:4)

假设ULL类型的宽度为64位,则表示您处于未定义的行为区域。根据{{​​1}}:

  

如果右操作数的值为负或大于或等于提升的左操作数的宽度,,则行为未定义。

可能发生的是移位值以64为模减少,C11 6.5.7 Bitwise shift operators为零。因此,它只是评估包裹到64 % 64的{​​{1}}。但是,老实说,由于UB没有施加任何真正的限制,它可能只是从空中榨取结果: - )

答案 2 :(得分:1)

1ULL&lt;&lt; 64未定义,而定义移位较少。

有关语言标准的详细信息,请参阅Is Shifting more than 32 bits of a uint64_t integer on an x86 machine Undefined Behavior?

答案 3 :(得分:0)

您达到了64位(长很长)的位限制。显然,左移2 ^ 63会导致翻转,导致2 ^ 0 = 1。

1 - 2 = -1当被视为无符号时(假设为long long)为2 ^ 64-1。

这个结果很有意思,因为左移的非翻转实现会导致0而不是1,这在减法后会产生2 ^ 64-2。