Почему операция сдвига ниже работает и в конечном итоге равна? Есть ли какое-нибудь название у этого узора? Я пытаюсь выяснить, что творилось в голове у человека, написавшего этот код!

int i = 0x1;
i |= 0x1 << 1;
i |= 0x1 << 2;
i |= 0x1 << 3;
i |= 0x1 << 4;
i |= 0x1 << 5;

int j = 5;

if( ((0x1 << (j + 1)) - 1) == i)
{
    // WHY?
}

Я пытался проверить, верно ли это для всех чисел, но работает только до 31.

for (int i = 1; i <= 100; i++) {
    int total_1 = 0x1;
    for (int j = 1; j <= i; j++) {
        total_1 |= 0x1 << j;
    }

    int total_2 = (0x1 << (i + 1)) - 1;
    if (total_2 == total_1) {
    } else {
        cout << i << endl;
        break;
    }
}

ОБНОВЛЕНИЕ

Пожалуйста, объясните первую часть, почему они в конечном итоге равны?

-4
pmoubed 4 Фев 2022 в 03:17
6
Популярный вопрос: если у вас всего 32 бита в int, что именно вы ожидаете, когда пытаетесь сдвинуть больше битов, чем может быть сдвинуто?
 – 
Sam Varshavchik
4 Фев 2022 в 03:18
3
Как вы думаете, что на самом деле делает этот оператор? Если вы этого не понимаете, вы пробовали искать документацию? Вы пытались ввести c shift operator в поисковую систему? Что-нибудь еще? По-вашему, что такое бинарный и как он используется для представления целочисленных значений? Если у вас есть целое число, записанное в двоичном формате, например 11111, как вы ожидаете, что будет выглядеть следующее большее целое число? Почему?
 – 
Karl Knechtel
4 Фев 2022 в 03:21
1
Сдвиг на количество битов в типе или более имеет неопределенное поведение как в C, так и в C++.
 – 
user17732522
4 Фев 2022 в 03:26
Кто-нибудь может объяснить первую часть? почему ((0x1 << (j + 1)) - 1) == i я получил 31 часть - спасибо
 – 
pmoubed
4 Фев 2022 в 03:27
1
— Я ожидаю, что результат будет нулевым, но некоторые составители не согласны. <г>
 – 
Pete Becker
4 Фев 2022 в 03:33

2 ответа

Лучший ответ

Есть ли какое-нибудь название у этого узора?

0x1u << pos (или просто 1u << pos) — это шаблон для получения числа, в котором установлен только бит в позиции pos. Использование подписанного 0x1 обычно является антишаблоном.

i |= 1u << pos — это шаблон для установки бита в позицию pos целого числа i.

(1u << pos) - 1 — это шаблон для создания набора битов только в позициях, меньших, чем pos.

Почему операция сдвига ниже работает и в конечном итоге равна?

Возможно, это может помочь взглянуть на промежуточные результаты:

                     // least significant byte
int i = 0x1;         // 0b0000'0001
i |= 0x1 << 1;       // 0b0000'0011
i |= 0x1 << 2;       // 0b0000'0111
i |= 0x1 << 3;       // 0b0000'1111
i |= 0x1 << 4;       // 0b0001'1111
i |= 0x1 << 5;       // 0b0011'1111

int      j = 5;
 0x1 << (j + 1)
 0x1 <<    6         // 0b0100'0000
(0x1 << (j + 1)) - 1 
 0b0100'0000     - 1 // 0b0011'1111

работает только до 31

int, вероятно, имеет ширину 32 бита в вашей системе. Если вы сдвинете 32-битный 0x1 на 31 или больше, то поведение программы будет неопределенным. Если бы вы использовали 0x1u, вы могли бы сдвинуться на 31, но 32 и выше были бы UB.

2
eerorika 4 Фев 2022 в 03:49

ОБНОВЛЕНИЕ Пожалуйста, объясните первую часть, почему они окажутся равными?

((0x1 << (j + 1)) - 1) устанавливает j младших битов в 1

Примере:

Если j равно 3, j+1 равно 4, 1 со сдвигом на 4 равно 0b1000; вычтите 1 - вы получите 0b0111

Re: Is there any name for this pattern? - я посмотрел здесь, этот шаблон используется пару раз, но не называется. Наверное, слишком очевидно :)

2
Vlad Feinstein 4 Фев 2022 в 04:02