Побитовые операции — различия между версиями
Penguinni (обсуждение | вклад) |
Penguinni (обсуждение | вклад) |
||
Строка 1: | Строка 1: | ||
− | '''Побитовые операции''' (англ. ''bitwise operations'') — операции, производимые над цепочками битов. Выделяют два типа побитовых операций: логические операции и побитовые сдвиги. | + | '''Побитовые операции''' (англ. ''bitwise operations'') — операции, производимые над цепочками битов. Выделяют два типа побитовых операций: [[Определение булевой функции | логические операции]] и побитовые сдвиги. |
==Принцип работы== | ==Принцип работы== | ||
===Логические побитовые операции=== | ===Логические побитовые операции=== | ||
− | Битовые операторы И <tex>(AND, \&)</tex>, ИЛИ <tex>(OR, \mid)</tex>, НЕ <tex>(NOT, \sim)</tex> и исключающее ИЛИ <tex>(XOR, $\textasciicircum$, \oplus)</tex> используют те же таблицы истинности, что и их логические эквиваленты. | + | Битовые операторы И <tex>(AND,\ \&)</tex>, ИЛИ <tex>(OR,\ \mid)</tex>, НЕ <tex>(NOT,\ \sim)</tex> и исключающее ИЛИ <tex>(XOR,\ $\textasciicircum$,\ \oplus)</tex> используют те же таблицы истинности, что и их логические эквиваленты. |
====Побитовое И==== | ====Побитовое И==== | ||
Побитовое И используется для выключения битов. Любой бит, установленный в <tex>0</tex>, вызывает установку соответствующего бита результата также в <tex>0</tex>. | Побитовое И используется для выключения битов. Любой бит, установленный в <tex>0</tex>, вызывает установку соответствующего бита результата также в <tex>0</tex>. | ||
Строка 44: | Строка 44: | ||
===Побитовые сдвиги=== | ===Побитовые сдвиги=== | ||
− | Операторы сдвига <tex>\texttt{<<}</tex> и <tex>\texttt{>>}</tex> сдвигают биты в переменной влево или вправо на указанное число. При этом на освободившиеся позиции устанавливаются нули (кроме сдвига вправо отрицательного числа, в этом случае на свободные позиции устанавливаются единицы, так как | + | Операторы сдвига <tex>\texttt{<<}</tex> и <tex>\texttt{>>}</tex> сдвигают биты в переменной влево или вправо на указанное число. При этом на освободившиеся позиции устанавливаются нули (кроме сдвига вправо отрицательного числа, в этом случае на свободные позиции устанавливаются единицы, так как числа представляются в [[Представление целых чисел: прямой код, код со сдвигом, дополнительный код #Дополнительный код (дополнение до двух) | двоичном дополнительном коде]] и необходимо поддерживать знаковый бит). |
Сдвиг влево может применяться для умножения числа на два, сдвиг вправо — для деления. | Сдвиг влево может применяться для умножения числа на два, сдвиг вправо — для деления. | ||
<code> | <code> | ||
− | x = 7 | + | x = 7 <font color = green>// 00000111 (7)</font> |
− | x << 1 <font color = green>// | + | x = x >> 1 <font color = green>// 00000011 (3)</font> |
− | x << 5 <font color = green>//11000000</font> | + | x = x << 1 <font color = green>// 00000110 (6)</font> |
− | x >> 2 <font color = green>// | + | x = x << 5 <font color = green>// 11000000 (-64)</font> |
+ | x = x >> 2 <font color = green>// 11110000 (-16)</font> | ||
</code> | </code> | ||
В языке программирования Java существует также оператор беззнакового битового сдвига вправо <tex>\texttt{>>>}</tex>. При использовании этого оператора на освободившиеся позиции всегда устанавливаются нули. | В языке программирования Java существует также оператор беззнакового битового сдвига вправо <tex>\texttt{>>>}</tex>. При использовании этого оператора на освободившиеся позиции всегда устанавливаются нули. | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
<code> | <code> | ||
− | + | x = 7 <font color = green>// 00000111 (7)</font> | |
− | + | x = x << 5 <font color = green>// 11100000 (-32)</font> | |
− | + | x = x >>> 2 <font color = green>// 00111000 (56)</font> | |
</code> | </code> | ||
− | + | ==Применение== | |
+ | ===Сложные операции=== | ||
+ | ====Проверка на то, является ли число степенью двойки==== | ||
− | |||
− | |||
− | |||
<code> | <code> | ||
− | + | answer = x '''and''' '''not'''(x & (x - 1)) <font color = green>// если answer == 1, то число x является степенью двойки</font> | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | '' | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
</code> | </code> | ||
− | = | + | ====Определение знака числа==== |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | ===Определение знака числа=== | ||
Поскольку при сдвиге вправо на освобождающиеся позиции устанавливается бит знака, знак числа можно определить следующим образом: | Поскольку при сдвиге вправо на освобождающиеся позиции устанавливается бит знака, знак числа можно определить следующим образом: | ||
<code> | <code> | ||
Строка 141: | Строка 84: | ||
'''bool''' result = ((x <tex>\oplus</tex> y) < 0) <font color = green>// значение переменной result == ''true'', если знаки переменных x и y различны</font> | '''bool''' result = ((x <tex>\oplus</tex> y) < 0) <font color = green>// значение переменной result == ''true'', если знаки переменных x и y различны</font> | ||
</code> | </code> | ||
− | ===Вычисление модуля числа без использования условного оператора=== | + | |
+ | ====Вычисление модуля числа без использования условного оператора==== | ||
<code> | <code> | ||
mask = x >> ''sizeof''('''int''') * '''CHAR_BIT''' - 1 | mask = x >> ''sizeof''('''int''') * '''CHAR_BIT''' - 1 | ||
Строка 149: | Строка 93: | ||
abs = (x <tex>\oplus</tex> mask) - mask | abs = (x <tex>\oplus</tex> mask) - mask | ||
</code> | </code> | ||
− | ===Нахождение минимума и максимума из двух чисел без использования условного оператора=== | + | |
+ | ====Нахождение минимума и максимума из двух чисел без использования условного оператора==== | ||
Этот способ корректен только если можно утверждать, что величина <tex>(x - y)</tex> лежит между граничными значениями типа int. | Этот способ корректен только если можно утверждать, что величина <tex>(x - y)</tex> лежит между граничными значениями типа int. | ||
<code> | <code> | ||
Строка 155: | Строка 100: | ||
max = x - ((x - y) & ((x - y) >> (''sizeof''('''int''') * '''CHAR_BIT''' - 1))) | max = x - ((x - y) & ((x - y) >> (''sizeof''('''int''') * '''CHAR_BIT''' - 1))) | ||
</code> | </code> | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | + | ===Применение для решения задач=== | |
− | + | ====Работа с битовыми масками==== | |
− | + | Для работы с подмножествами удобно использовать битовые маски. Применяя побитовые операции легко сделать следующее: найти дополнение <tex>(\sim mask)</tex>, пересечение <tex>(mask_1\ \&\ mask_2)</tex>, объединение <tex>(mask_1 \mid mask_2)</tex> множеств, установить бит по номеру <tex>(mask \mid (1\ \texttt{<<}\ x))</tex>, снять бит по номеру <tex>(mask\ \&\ \sim(1\ \texttt{<<}\ x))</tex>. | |
− | + | ||
− | + | Битовые маски используются, например, при решении некоторых задач<ref>[[Гамильтоновы графы #Задача о коммивояжере| Динамическое программирование по подмножествам (по маскам)]]</ref> [[Динамическое программирование | динамического программирования]]. | |
− | + | ||
− | + | ====Алгоритм Флойда==== | |
− | + | {{main|Алгоритм Флойда}} | |
− | + | ||
+ | ====Дерево Фенвика==== | ||
+ | {{main|Дерево Фенвика}} | ||
− | == | + | ==Примечания== |
− | + | <references/> | |
==Источники информации== | ==Источники информации== | ||
* [http://www.c-cpp.ru/books/bitovye-operatory Онлайн справочник программиста на С и С++] | * [http://www.c-cpp.ru/books/bitovye-operatory Онлайн справочник программиста на С и С++] | ||
− | |||
− | |||
* [http://developer.alexanderklimov.ru/android/java/bitwise.php Побитовые операторы] | * [http://developer.alexanderklimov.ru/android/java/bitwise.php Побитовые операторы] | ||
* [https://graphics.stanford.edu/~seander/bithacks.html Bit Twiddling Hacks by Sean Eron Anderson] | * [https://graphics.stanford.edu/~seander/bithacks.html Bit Twiddling Hacks by Sean Eron Anderson] |
Версия 02:54, 7 марта 2016
Побитовые операции (англ. bitwise operations) — операции, производимые над цепочками битов. Выделяют два типа побитовых операций: логические операции и побитовые сдвиги.
Содержание
Принцип работы
Логические побитовые операции
Битовые операторы И
, ИЛИ , НЕ и исключающее ИЛИ используют те же таблицы истинности, что и их логические эквиваленты.Побитовое И
Побитовое И используется для выключения битов. Любой бит, установленный в
, вызывает установку соответствующего бита результата также в .& | |
---|---|
11001010 11100010 | |
11000010 |
Побитовое ИЛИ
Побитовое ИЛИ используется для включения битов. Любой бит, установленный в
, вызывает установку соответствующего бита результата также в .| | |
---|---|
11001010 11100010 | |
11101010 |
Побитовое НЕ
Побитовое НЕ инвертирует состояние каждого бита исходной переменной.
~ | |
---|---|
11001010 | |
00110101 |
Побитовое исключающее ИЛИ
Исключающее ИЛИ устанавливает значение бита результата в
, если значения в соответствующих битах исходных переменных различны.^ | |
---|---|
11001010 11100010 | |
00101000 |
Побитовые сдвиги
Операторы сдвига двоичном дополнительном коде и необходимо поддерживать знаковый бит).
и сдвигают биты в переменной влево или вправо на указанное число. При этом на освободившиеся позиции устанавливаются нули (кроме сдвига вправо отрицательного числа, в этом случае на свободные позиции устанавливаются единицы, так как числа представляются вСдвиг влево может применяться для умножения числа на два, сдвиг вправо — для деления.
x = 7 // 00000111 (7) x = x >> 1 // 00000011 (3) x = x << 1 // 00000110 (6) x = x << 5 // 11000000 (-64) x = x >> 2 // 11110000 (-16)
В языке программирования Java существует также оператор беззнакового битового сдвига вправо
. При использовании этого оператора на освободившиеся позиции всегда устанавливаются нули.
x = 7 // 00000111 (7) x = x << 5 // 11100000 (-32) x = x >>> 2 // 00111000 (56)
Применение
Сложные операции
Проверка на то, является ли число степенью двойки
answer = x and not(x & (x - 1)) // если answer == 1, то число x является степенью двойки
Определение знака числа
Поскольку при сдвиге вправо на освобождающиеся позиции устанавливается бит знака, знак числа можно определить следующим образом:
// в этом и следующих примерах в константе CHAR_BIT хранится количество битов в одном байте sign = x >> (sizeof(int) * CHAR_BIT - 1) // если x < 0, результатом будет -1, иначе 0
sign = (x != 0) | (x >> (sizeof(int) * CHAR_BIT - 1)) // результатом будет -1, 0, или +1 // для отрицательного, равного нулю и положительного числа x соответственно
Используя побитовые операции можно также узнать, различны ли знаки двух переменных:
bool result = ((x
y) < 0) // значение переменной result == true, если знаки переменных x и y различны
Вычисление модуля числа без использования условного оператора
mask = x >> sizeof(int) * CHAR_BIT - 1 abs = (x + mask)mask // другой способ сделать то же самое: abs = (x mask) - mask
Нахождение минимума и максимума из двух чисел без использования условного оператора
Этот способ корректен только если можно утверждать, что величина
min = y + ((x - y) & ((x - y) >> (sizeof(int) * CHAR_BIT - 1))) max = x - ((x - y) & ((x - y) >> (sizeof(int) * CHAR_BIT - 1)))
Применение для решения задач
Работа с битовыми масками
Для работы с подмножествами удобно использовать битовые маски. Применяя побитовые операции легко сделать следующее: найти дополнение
, пересечение , объединение множеств, установить бит по номеру , снять бит по номеру .Битовые маски используются, например, при решении некоторых задач[1] динамического программирования.