Как в C++ получить число, где все биты нулевые, кроме одного заданного разряда?

Ответ

Для создания битовой маски с единицей в заданной позиции используется оператор левого сдвига (<<).

Базовый пример:

#include <iostream>
#include <cstdint> // Для точных типов

int main() {
    int bit_position = 3; // Нумерация с 0 (младший бит)
    int mask = 1 << bit_position; // 1 << 3 = 0b1000 (десятичное 8)
    std::cout << "Mask (dec): " << mask << std::endl; // 8
    std::cout << "Mask (hex): 0x" << std::hex << mask << std::endl; // 0x8

    // Использование для проверки бита в числе
    int value = 13; // 0b1101
    bool is_bit_set = (value & mask) != 0;
    std::cout << "Bit at position " << bit_position << " is " << (is_bit_set ? "set" : "not set") << std::endl;
    return 0;
}

Критически важные нюансы:

  1. Переполнение и неопределённое поведение: Сдвиг на позицию, большую или равную ширине типа, приводит к UB.
    int x = 1 << 32; // UB для 32-битного int!
  2. Используйте беззнаковые типы: Для битовых операций всегда предпочтительнее беззнаковые типы, так как сдвиги знаковых чисел могут иметь платформо-зависимое поведение.
    unsigned int safe_mask = 1U << bit_position;
  3. Для 64-битных масок: Используйте суффикс ULL.
    uint64_t wide_mask = 1ULL << 45; // Безопасно для позиций > 31
  4. Портируемый способ с проверкой:
    
    #include <climits>

template T make_bitmask(unsigned int pos) { if (pos >= sizeof(T) * CHAR_BIT) { return 0; // Или выбросить исключение } return static_cast(T{1} << pos); } // Использование: auto mask32 = make_bitmask(5); auto mask64 = make_bitmask(40);

Ответ 18+ 🔞

А, ну это ж классика, ёпта! Сейчас объясню, как эти битовые маски делать, чтобы потом не охуеть от багов.

Смотри, в основе всего лежит оператор левого сдвига — <<. Представь, что у тебя есть единичка, и ты её толкаешь влево на определённое количество позиций. Всё просто, как три копейки.

Вот самый базовый пример, с которого всё начинается:

#include <iostream>
#include <cstdint> // Для точных типов

int main() {
    int bit_position = 3; // Нумерация с 0 (младший бит)
    int mask = 1 << bit_position; // 1 << 3 = 0b1000 (десятичное 8)
    std::cout << "Mask (dec): " << mask << std::endl; // 8
    std::cout << "Mask (hex): 0x" << std::hex << mask << std::endl; // 0x8

    // Использование для проверки бита в числе
    int value = 13; // 0b1101
    bool is_bit_set = (value & mask) != 0;
    std::cout << "Bit at position " << bit_position << " is " << (is_bit_set ? "set" : "not set") << std::endl;
    return 0;
}

А теперь, чувак, самое важное — нюансы, на которых все обламываются. Вот где собака зарыта, ядрёна вошь!

  1. Переполнение и неопределённое поведение — пиздец полный. Это главная ловушка. Если сдвинешь единицу на позицию, которая больше или равна разрядности твоего типа, получишь UB. Программа может сделать что угодно: скомпилироваться и молча сломаться, выдать мусор или вообще улететь в космос.

    int x = 1 << 32; // UB для 32-битного int! Вот тут и накрывается медным тазом твой код.
  2. Используй беззнаковые типы, не будь распиздяем. Для битовых операций — только unsigned. Со знаковыми типами (int, long) сдвиги — это тёмный лес, поведение зависит от компилятора и стандарта. Не надо так.

    unsigned int safe_mask = 1U << bit_position; // Вот так — правильно.
  3. Когда дело доходит до 64 бит — не забудь суффикс ULL. Иначе опять попадёшь на переполнение. Хочешь сдвинуть на 45-ю позицию? Пиши 1ULL.

    uint64_t wide_mask = 1ULL << 45; // Безопасно для позиций > 31. Всё чётко.
  4. Портируемый и надёжный способ, чтобы спать спокойно. Оберни эту логику в шаблон с проверкой. Тогда волнение ебать будет нулевое.

    
    #include <climits>

template T make_bitmask(unsigned int pos) { if (pos >= sizeof(T) * CHAR_BIT) { return 0; // Или выбросить исключение — решай сам. } return static_cast(T{1} << pos); } // Использование: auto mask32 = make_bitmask(5); auto mask64 = make_bitmask(40);



Вот и вся магия. Главное — помни про переполнение и используй беззнаковые типы, а то будет тебе хиросима, а не битовая маска.