Подтвердить что ты не робот

Как создать маску с наименьшими значащими битами, установленными в 1 в C

Может кто-нибудь объяснит мне эту функцию?

Маска с наименее значимыми n битами, установленными в 1.

Пример:

n = 6 → 0x2F, n = 17 → 0x1FFFF//Я не получаю их вообще, особенно, как n = 6 → 0x2F

Кроме того, что такое маска?

4b9b3361

Ответ 1

Обычный способ - взять 1 и сдвинуть его налево n бит. Это даст вам что-то вроде: 00100000. Затем вычитайте один из них, который очистит бит, который установлен, и установите все менее значимые бит, поэтому в этом случае мы получим: 00011111.

Маска обычно используется с побитовыми операциями, особенно and. Вы использовали бы маску выше, чтобы получить 5 наименее значимых бит самостоятельно, изолированный от всего остального, что может присутствовать. Это особенно часто встречается при работе с оборудованием, которое часто имеет один аппаратный регистр, содержащий биты, представляющие собой множество полностью отдельных, несвязанных величин и/или флагов.

Ответ 2

Как по правильности, так и по производительности, лучший способ добиться этого изменился, поскольку этот вопрос был задан еще в 2012 году из-за появления инструкций BMI в современных процессорах x86, в частности BLSMSK.

Здесь хороший способ приблизиться к этой проблеме, сохраняя обратную совместимость со старыми процессорами.

Этот метод правильный, тогда как текущие верхние ответы приводят к поведению undefined в случаях краев.

Clang и GCC, когда им разрешено оптимизировать использование инструкций BMI, будут конденсировать gen_mask() только с двумя операциями. При поддержке аппаратного обеспечения обязательно добавьте флаги компилятора для инструкций BMI: -mbmi -mbmi2

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

uint64_t gen_mask(const uint_fast8_t msb) {
  const uint64_t src = (uint64_t)1  << msb;
  return (src - 1) ^ src;
}

int main() {
  uint_fast8_t msb;
  for (msb = 0; msb < 64; ++msb) {
    printf("%016" PRIx64 "\n", gen_mask(msb));
  }
  return 0;
}

Ответ 3

Маска является общим термином для целочисленного значения, которое является битовым ANDed, ORed, XORed и т.д. с другим целым значением.

Например, если вы хотите извлечь 8 наименее значимых цифр переменной int, вы делаете variable & 0xFF. 0xFF - это маска.

Аналогично, если вы хотите установить биты 0 и 8, вы делаете variable | 0x101, где 0x101 является маской.

Или, если вы хотите инвертировать одни и те же биты, вы делаете variable ^ 0x101, где 0x101 является маской.

Чтобы сгенерировать маску для вашего случая, вы должны использовать простой математический факт, что если вы добавите 1 к своей маске (маска со всеми ее младшими значащими битами, установленными в 1, а остальные - 0), вы получите значение, которое мощность 2.

Итак, если вы создаете ближайшую силу 2, вы можете вычесть 1 из нее, чтобы получить маску.

Положительные степени 2 легко сгенерированы с помощью оператора сдвига влево. << в C.

Следовательно, 1 << n дает 2 n. В двоичном формате это 10... 0 с n 0s.

(1 << n) - 1 создаст маску с n младшими битами, установленными в 1.

Теперь вам нужно следить за переполнениями в левых сменах. В C (и на С++) вы не можете законно сдвигать переменную, оставшуюся на столько битовых позиций, что и переменная, поэтому, если ints 32-бит, 1<<32 приводит к undefined behavior. Также следует избегать переполнения целых чисел, поэтому следует использовать неподписанные значения, например. 1u << 31.

Ответ 4

Я считаю, что ваш первый пример должен быть 0x3f.

0x3f представляет собой шестнадцатеричную нотацию для числа 63, который является 111111 в двоичном формате, так что последние 6 бит (наименее значимые 6 бит) установлены на 1.

Следующая небольшая программа C рассчитает правильную маску:

#include <stdarg.h>
#include <stdio.h>

int mask_for_n_bits(int n)
{
    int mask = 0;

    for (int i = 0; i < n; ++i)
        mask |= 1 << i;

    return mask;
}

int main (int argc, char const *argv[])
{
    printf("6: 0x%x\n17: 0x%x\n", mask_for_n_bits(6), mask_for_n_bits(17));
    return 0;
}

Ответ 5

0x2F является 0010 1111 в двоичном формате - это должно быть 0x3f, которое является 0011 1111 в двоичном формате и которое имеет 6 наименьших значащих бит.

Аналогично, 0x1FFFF есть 0001 1111 1111 1111 1111 в двоичном формате, в котором установлено 17 наименее значимых бит.

"Маска" - это значение, которое должно сочетаться с другим значением с использованием побитового оператора, такого как &, | или ^, чтобы индивидуально устанавливать, отменять, переворачивать или оставлять без изменений биты в этом другом значение.

Например, если вы объедините маску 0x2F с некоторым значением n с помощью оператора &, результат будет иметь нули во всех, кроме 6 наименее значимых бит, и эти 6 бит будут скопированы без изменений из значение n.

В случае маски & двоичный 0 в маске означает "безоговорочно установить бит результата в 0", а 1 означает "установить бит результата в бит входного значения". Для маски |, 0 в маске задает бит результата для входного бита, а 1 безоговорочно устанавливает бит результата в 1, а для маски ^ - набор 0 бит результата для входного бита и 1 устанавливает бит результата в дополнение к входному биту.

Ответ 6

TL; DR

Во-первых, для тех, кому нужен только код для создания маски:

uint64_t bits = 6;
uint64_t mask = ((uint64_t)1 << bits) - 1;
# Results in 0b111111 (or 0x03F)

Длинная версия:

Маска - это обычно имя для значения, которое мы используем для манипулирования другими значениями с помощью побитовых операций, таких как AND, OR, XOR и т.д.

Короткие маски обычно представлены в двоичном виде, где мы можем явно видеть все биты, которые установлены в 1.

Более длинные маски обычно представлены в шестнадцатеричном формате, что очень легко прочитать, как только вы овладеете им.

Вы можете прочитать больше о побитовых операциях в C здесь, вы получите лучшее понимание материала.