Может кто-нибудь объяснит мне эту функцию?
Маска с наименее значимыми n битами, установленными в 1.
Пример:
n = 6 → 0x2F, n = 17 → 0x1FFFF//Я не получаю их вообще, особенно, как n = 6 → 0x2F
Кроме того, что такое маска?
Может кто-нибудь объяснит мне эту функцию?
Маска с наименее значимыми n битами, установленными в 1.
Пример:
n = 6 → 0x2F, n = 17 → 0x1FFFF//Я не получаю их вообще, особенно, как n = 6 → 0x2F
Кроме того, что такое маска?
Обычный способ - взять 1
и сдвинуть его налево n
бит. Это даст вам что-то вроде: 00100000
. Затем вычитайте один из них, который очистит бит, который установлен, и установите все менее значимые бит, поэтому в этом случае мы получим: 00011111
.
Маска обычно используется с побитовыми операциями, особенно and
. Вы использовали бы маску выше, чтобы получить 5 наименее значимых бит самостоятельно, изолированный от всего остального, что может присутствовать. Это особенно часто встречается при работе с оборудованием, которое часто имеет один аппаратный регистр, содержащий биты, представляющие собой множество полностью отдельных, несвязанных величин и/или флагов.
Как по правильности, так и по производительности, лучший способ добиться этого изменился, поскольку этот вопрос был задан еще в 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;
}
Маска является общим термином для целочисленного значения, которое является битовым 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
.
Я считаю, что ваш первый пример должен быть 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;
}
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
устанавливает бит результата в дополнение к входному биту.
TL; DR
Во-первых, для тех, кому нужен только код для создания маски:
uint64_t bits = 6;
uint64_t mask = ((uint64_t)1 << bits) - 1;
# Results in 0b111111 (or 0x03F)
Длинная версия:
Маска - это обычно имя для значения, которое мы используем для манипулирования другими значениями с помощью побитовых операций, таких как AND, OR, XOR и т.д.
Короткие маски обычно представлены в двоичном виде, где мы можем явно видеть все биты, которые установлены в 1.
Более длинные маски обычно представлены в шестнадцатеричном формате, что очень легко прочитать, как только вы овладеете им.
Вы можете прочитать больше о побитовых операциях в C здесь, вы получите лучшее понимание материала.