为什么0x7FFFFFFFull | (1 << 31) 在 C++ 中返回 0xFFFFFFFFFFFFFFFF?
Why is 0x7FFFFFFFull | (1 << 31) returning 0xFFFFFFFFFFFFFFFF in C++?
当我执行 (0x7fffffff | 0x8000000) 时,我得到的是 0xffffffffffffffff 而不是预期的 0xffffffff。我错过了什么?
一些示例代码和输出来说明我的问题。
代码:
#include <iostream>
using namespace std;
int main()
{
unsigned long long val = 0;
for (int i = 0; i < 64; i++) {
val |= 0x1 << i;
cout << i << ": " << std::hex << val << std::dec << endl;
}
return 0;
}
输出:
0: 1
1: 3
2: 7
3: f
4: 1f
5: 3f
6: 7f
7: ff
8: 1ff
9: 3ff
10: 7ff
11: fff
12: 1fff
13: 3fff
14: 7fff
15: ffff
16: 1ffff
17: 3ffff
18: 7ffff
19: fffff
20: 1fffff
21: 3fffff
22: 7fffff
23: ffffff
24: 1ffffff
25: 3ffffff
26: 7ffffff
27: fffffff
28: 1fffffff
29: 3fffffff
30: 7fffffff
31: ffffffffffffffff
32: ffffffffffffffff
33: ffffffffffffffff
34: ffffffffffffffff
35: ffffffffffffffff
36: ffffffffffffffff
37: ffffffffffffffff
38: ffffffffffffffff
39: ffffffffffffffff
40: ffffffffffffffff
41: ffffffffffffffff
42: ffffffffffffffff
43: ffffffffffffffff
44: ffffffffffffffff
45: ffffffffffffffff
46: ffffffffffffffff
47: ffffffffffffffff
48: ffffffffffffffff
49: ffffffffffffffff
50: ffffffffffffffff
51: ffffffffffffffff
52: ffffffffffffffff
53: ffffffffffffffff
54: ffffffffffffffff
55: ffffffffffffffff
56: ffffffffffffffff
57: ffffffffffffffff
58: ffffffffffffffff
59: ffffffffffffffff
60: ffffffffffffffff
61: ffffffffffffffff
62: ffffffffffffffff
63: ffffffffffffffff
因为符号扩展 val
是一个 unsigned long long。但是 val 不是你要改变的。您正在移动 0x1,一个 int,然后将其扩展为 long long for the '|='
首先,您的代码没有按照您在 title/question 中所说的执行;我已经编辑了标题。
问题是1 << 31
。如果您有 32 位 int
(从结果来看,显然您有),这会导致算术溢出。在 C++14 中,这是 implementation-defined 行为;在 C++14 之前,它会导致未定义的行为。 Reference.
通常 implementation-defined 行为将生成设置了符号位且未设置其他位的 int。在 2 的补码中,该值为 INT_MIN
。然后,您在 unsigned long long
和 int
之间执行算术运算。这定义为:int
转换为 unsigned long long
.
将有符号整数转换为无符号整数是通过将其环绕(模块化算术)模 ULLONG_MAX+1
来完成的。所以(unsigned long long)INT_MIN
的结果是一个非常大的正数,实际上是0xFFFFFFFF80000000
。 (要检查这一点,请向其中添加 0x80000000
以获得 0
)。
所以你实际上是在做 0x7FFFFFFF | 0xFFFFFFFF80000000
这给出了观察到的结果。
稍后情况会变得更糟:1 << 32
和更大的字符会由于移动整个字体宽度而导致未定义的行为。
要解决此问题,请将代码中的 0x1
更改为 1ull
。您将移动 unsigned long long
,而不是 int
。
当我执行 (0x7fffffff | 0x8000000) 时,我得到的是 0xffffffffffffffff 而不是预期的 0xffffffff。我错过了什么?
一些示例代码和输出来说明我的问题。
代码:
#include <iostream>
using namespace std;
int main()
{
unsigned long long val = 0;
for (int i = 0; i < 64; i++) {
val |= 0x1 << i;
cout << i << ": " << std::hex << val << std::dec << endl;
}
return 0;
}
输出:
0: 1 1: 3 2: 7 3: f 4: 1f 5: 3f 6: 7f 7: ff 8: 1ff 9: 3ff 10: 7ff 11: fff 12: 1fff 13: 3fff 14: 7fff 15: ffff 16: 1ffff 17: 3ffff 18: 7ffff 19: fffff 20: 1fffff 21: 3fffff 22: 7fffff 23: ffffff 24: 1ffffff 25: 3ffffff 26: 7ffffff 27: fffffff 28: 1fffffff 29: 3fffffff 30: 7fffffff 31: ffffffffffffffff 32: ffffffffffffffff 33: ffffffffffffffff 34: ffffffffffffffff 35: ffffffffffffffff 36: ffffffffffffffff 37: ffffffffffffffff 38: ffffffffffffffff 39: ffffffffffffffff 40: ffffffffffffffff 41: ffffffffffffffff 42: ffffffffffffffff 43: ffffffffffffffff 44: ffffffffffffffff 45: ffffffffffffffff 46: ffffffffffffffff 47: ffffffffffffffff 48: ffffffffffffffff 49: ffffffffffffffff 50: ffffffffffffffff 51: ffffffffffffffff 52: ffffffffffffffff 53: ffffffffffffffff 54: ffffffffffffffff 55: ffffffffffffffff 56: ffffffffffffffff 57: ffffffffffffffff 58: ffffffffffffffff 59: ffffffffffffffff 60: ffffffffffffffff 61: ffffffffffffffff 62: ffffffffffffffff 63: ffffffffffffffff
因为符号扩展 val
是一个 unsigned long long。但是 val 不是你要改变的。您正在移动 0x1,一个 int,然后将其扩展为 long long for the '|='
首先,您的代码没有按照您在 title/question 中所说的执行;我已经编辑了标题。
问题是1 << 31
。如果您有 32 位 int
(从结果来看,显然您有),这会导致算术溢出。在 C++14 中,这是 implementation-defined 行为;在 C++14 之前,它会导致未定义的行为。 Reference.
通常 implementation-defined 行为将生成设置了符号位且未设置其他位的 int。在 2 的补码中,该值为 INT_MIN
。然后,您在 unsigned long long
和 int
之间执行算术运算。这定义为:int
转换为 unsigned long long
.
将有符号整数转换为无符号整数是通过将其环绕(模块化算术)模 ULLONG_MAX+1
来完成的。所以(unsigned long long)INT_MIN
的结果是一个非常大的正数,实际上是0xFFFFFFFF80000000
。 (要检查这一点,请向其中添加 0x80000000
以获得 0
)。
所以你实际上是在做 0x7FFFFFFF | 0xFFFFFFFF80000000
这给出了观察到的结果。
稍后情况会变得更糟:1 << 32
和更大的字符会由于移动整个字体宽度而导致未定义的行为。
要解决此问题,请将代码中的 0x1
更改为 1ull
。您将移动 unsigned long long
,而不是 int
。