Почему 0x7FFFFFFFull | (1 ‹---------------- 31) возвращение 0xFFFFFFFFFFFFFFFF в 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

person redmaw    schedule 18.06.2015    source источник


Ответы (2)


Во-первых, ваш код не выполняет то, что вы говорите в заголовке / вопросе; Я отредактировал заголовок.

Проблема в 1 << 31. Если у вас 32-битный int (что, судя по результатам, судя по всему, у вас есть), это вызывает арифметическое переполнение. В C ++ 14 это поведение определяется реализацией; до C ++ 14 это вызывает неопределенное поведение. Справочник.

Обычно поведение, определяемое реализацией, будет заключаться в генерации 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.

person M.M    schedule 18.06.2015

из-за расширения знака val является беззнаковым длинным длинным. Но val - это не то, что вы перекладываете. Вы перемещаете 0x1, int, а затем расширяете его до long для '| ='

person Christopher Ian Stern    schedule 18.06.2015