Побитовые операции — различия между версиями

Материал из Викиконспекты
Перейти к: навигация, поиск
Строка 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     <font color = green>//00000111</font>
+
  x = 7         <font color = green>// 00000111 (7)</font>
  x << 1    <font color = green>//00001110</font>
+
  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>//00110000</font>
+
  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>. При использовании этого оператора на освободившиеся позиции всегда устанавливаются нули.
  
====Ограничения====
 
При использовании битовых сдвигов для отрицательных чисел округление происходит к <tex>-\infty</tex>.
 
 
'''''C++ Visual Studio 15'''''
 
 
Если выполняется сдвиг влево числа со знаком и при этом затрагивается бит знака, результат не определен.
 
 
Результат сдвига вправо отрицательного числа со знаком зависит от реализации.
 
 
Результат операции сдвига также не определен, если число, на которое нужно сдвинуть биты, имеет отрицательное значение, или если оно больше или равно количеству битов в исходном числе.
 
 
<code>
 
<code>
    '''short''' x = 16384    <font color = green>// 01000000 00000000</font>
+
x = 7          <font color = green>// 00000111 (7)</font>
    '''short''' y = x << <font color = green>// 10000000 00000000</font>
+
x = x << 5    <font color = green>// 11100000 (-32)</font>
                      <font color = green>// 16384 left-shifted by 1 = -32768</font>
+
x = x >>> 2    <font color = green>// 00111000 (56)</font>
 
</code>
 
</code>
  
'''''Java'''''
+
==Применение==
 +
===Сложные операции===
 +
====Проверка на то, является ли число степенью двойки====
  
При сдвиге на количество бит большее, чем разрядность левого операнда, происходит неявное сокращение правого операнда (количество бит).
 
 
''Примеры:''
 
 
<code>
 
<code>
  i = 1      <font color = green>//00000000 00000000 00000000 00000001 (1)</font>
+
  answer = x '''and''' '''not'''(x & (x - 1)) <font color = green>// если answer == 1, то число x является степенью двойки</font>
i << 29    <font color = green>//00100000 00000000 00000000 00000000 (536870912)</font>
 
i << 30    <font color = green>//01000000 00000000 00000000 00000000 (1073741824)</font>
 
i << 31    <font color = green>//10000000 00000000 00000000 00000000 (-2147483648 / 2147483648)</font>
 
i << 32    <font color = green>//00000000 00000000 00000000 00000001 (1)</font>
 
</code>
 
 
 
<code>
 
i = -1      <font color = green>//11111111 11111111 11111111 11111111 (-1 / 4294967295)</font>
 
i >>> 1    <font color = green>//01111111 11111111 11111111 11111111 (2147483647)</font>
 
i >>> 30    <font color = green>//00000000 00000000 00000000 00000011 (3)</font>
 
i >>> 31    <font color = green>//00000000 00000000 00000000 00000001 (1)</font>
 
i >>> 32    <font color = green>//11111111 11111111 11111111 11111111 (-1 / 4294967295)</font>
 
</code>
 
 
 
<code>
 
i = -192    <font color = green>//11111111 11111111 11111111 01000000 (-192 / 4294967104)</font>
 
i >> 1      <font color = green>//11111111 11111111 11111111 10100000 (-96 / 4294967200)</font>
 
i >> 30    <font color = green>//11111111 11111111 11111111 11111111 (-1 / 4294967295)</font>
 
i >> 31    <font color = green>//11111111 11111111 11111111 11111111 (-1 / 4294967295)</font>
 
i >> 32    <font color = green>//11111111 11111111 11111111 01000000 (-192 / 4294967104)</font>
 
</code>
 
 
 
{{Acronym | Арифметическое распространение | Приведение меньшего типа данных к большему}} в Java проводится перед операциями и гарантирует расширение каждого операнда по крайней мере до int (или, если один из операндов имеет больший тип, то до него). Расширение происходит знаково, ввиду чего результат может быть не таким, как ожидалось; при приведении типа к меньшему лишние байты отбрасываются.
 
 
 
''Примеры:''
 
<code>
 
'''byte''' b = -127  <font color = green>//10000001 (-127 / 129)</font>
 
('''int''')b          <font color = green>//11111111 11111111 11111111 10000001 (-127 / 4294967169)</font>
 
 
 
'''int''' i = -127    <font color = green>//11111111 11111111 11111111 10000001 (-127 / 4294967169)</font>
 
('''byte''')i        <font color = green>//10000001 (-127 / 129)</font>
 
 
 
'''int''' i = 128    <font color = green>//00000000 00000000 00000000 10000000 (128)</font>
 
('''byte''')i        <font color = green>//10000000 (-128 / 128)</font>
 
 
 
'''int''' i = 256    <font color = green>//00000000 00000000 00000001 00000000 (256)</font>
 
('''byte''')i        <font color = green>//00000000 (0)</font>
 
 
 
'''int''' i = -256    <font color = green>//11111111 11111111 11111111 00000000 (-256 / 4294967040)</font>
 
('''byte''')i        <font color = green>//00000000 (0)</font>
 
 
</code>
 
</code>
  
==Применение для решения задач==
+
====Определение знака числа====
===Проверка на то, является ли число степенью двойки===
 
<code>
 
answer = x '''and''' '''not'''(x & (x - 1)) <font color = green>// если answer == 1, то число x является степенью двойки</font>
 
</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>.
 
===Определение знака числа===
 
 
Поскольку при сдвиге вправо на освобождающиеся позиции устанавливается бит знака, знак числа можно определить следующим образом:
 
Поскольку при сдвиге вправо на освобождающиеся позиции устанавливается бит знака, знак числа можно определить следующим образом:
 
<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>
===Кодирование информации===
 
<code>
 
<font color = green>// функция для шифровки текста с помощью XOR</font>
 
'''function''' encode(secret: '''string''', key: '''string'''): '''byte[]'''
 
    btxt = secret.''getBytes''
 
    bkey = key.''getBytes''
 
 
    '''for''' i = 0 .. btxt.''length'' - 1:
 
        result[i] = (btxt[i] <tex>\oplus</tex> bkey[i % bkey.''length'']) '''as''' '''byte'''
 
 
    '''return''' result
 
  
<font color = green>// функция для расшифровки текста</font>
+
===Применение для решения задач===
'''function''' decode(secret: '''byte[]''', key: '''string'''): '''string'''
+
====Работа с битовыми масками====
    bkey = key.''getBytes''
+
Для работы с подмножествами удобно использовать битовые маски. Применяя побитовые операции легко сделать следующее: найти дополнение <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>.
+
 
    '''for''' i = 0 .. secret.''length'' - 1:
+
Битовые маски используются, например, при решении некоторых задач<ref>[[Гамильтоновы графы #Задача о коммивояжере| Динамическое программирование по подмножествам (по маскам)]]</ref> [[Динамическое программирование | динамического программирования]].
        result[i] = (secret[i] <tex>\oplus</tex> bkey[i % bkey.''length'']) '''as''' '''byte'''
+
 
+
====Алгоритм Флойда====
    '''return''' result '''as''' '''string'''
+
{{main|Алгоритм Флойда}}
</code>
+
 
 +
====Дерево Фенвика====
 +
{{main|Дерево Фенвика}}
  
===[[Алгоритм Флойда#Оптимизация с помощью битовых масок | Алгоритм Флойда]]===
+
==Примечания==
===[[Дерево Фенвика]]===
+
<references/>
  
 
==Источники информации==
 
==Источники информации==
 
* [http://www.c-cpp.ru/books/bitovye-operatory Онлайн справочник программиста на С и С++]
 
* [http://www.c-cpp.ru/books/bitovye-operatory Онлайн справочник программиста на С и С++]
* [https://msdn.microsoft.com/ru-ru/library/336xbhcz.aspx MSDN: Операторы сдвигов влево и вправо]
 
* [http://dark-barker.blogspot.ru/2012/03/bit-operations-java-pitfalls.html Битовые сдвиги и приведения в Java: подводные камни]
 
 
* [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) — операции, производимые над цепочками битов. Выделяют два типа побитовых операций: логические операции и побитовые сдвиги.

Принцип работы

Логические побитовые операции

Битовые операторы И [math](AND,\ \&)[/math], ИЛИ [math](OR,\ \mid)[/math], НЕ [math](NOT,\ \sim)[/math] и исключающее ИЛИ [math](XOR,\ $\textasciicircum$,\ \oplus)[/math] используют те же таблицы истинности, что и их логические эквиваленты.

Побитовое И

Побитовое И используется для выключения битов. Любой бит, установленный в [math]0[/math], вызывает установку соответствующего бита результата также в [math]0[/math].

&
11001010
11100010
11000010

Побитовое ИЛИ

Побитовое ИЛИ используется для включения битов. Любой бит, установленный в [math]1[/math], вызывает установку соответствующего бита результата также в [math]1[/math].

|
11001010
11100010
11101010

Побитовое НЕ

Побитовое НЕ инвертирует состояние каждого бита исходной переменной.

~
11001010
00110101

Побитовое исключающее ИЛИ

Исключающее ИЛИ устанавливает значение бита результата в [math]1[/math], если значения в соответствующих битах исходных переменных различны.

^
11001010
11100010
00101000

Побитовые сдвиги

Операторы сдвига [math]\texttt{\lt \lt }[/math] и [math]\texttt{\gt \gt }[/math] сдвигают биты в переменной влево или вправо на указанное число. При этом на освободившиеся позиции устанавливаются нули (кроме сдвига вправо отрицательного числа, в этом случае на свободные позиции устанавливаются единицы, так как числа представляются в двоичном дополнительном коде и необходимо поддерживать знаковый бит).

Сдвиг влево может применяться для умножения числа на два, сдвиг вправо — для деления.

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 существует также оператор беззнакового битового сдвига вправо [math]\texttt{\gt \gt \gt }[/math]. При использовании этого оператора на освободившиеся позиции всегда устанавливаются нули.

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 [math]\oplus[/math] y) < 0)    // значение переменной result == true, если знаки переменных x и y различны

Вычисление модуля числа без использования условного оператора

mask = x >> sizeof(int) * CHAR_BIT - 1

abs = (x + mask) [math]\oplus[/math] mask
// другой способ сделать то же самое:
abs = (x [math]\oplus[/math] mask) - mask

Нахождение минимума и максимума из двух чисел без использования условного оператора

Этот способ корректен только если можно утверждать, что величина [math](x - y)[/math] лежит между граничными значениями типа int.

min = y + ((x - y) & ((x - y) >> (sizeof(int) * CHAR_BIT - 1)))
max = x - ((x - y) & ((x - y) >> (sizeof(int) * CHAR_BIT - 1)))

Применение для решения задач

Работа с битовыми масками

Для работы с подмножествами удобно использовать битовые маски. Применяя побитовые операции легко сделать следующее: найти дополнение [math](\sim mask)[/math], пересечение [math](mask_1\ \&\ mask_2)[/math], объединение [math](mask_1 \mid mask_2)[/math] множеств, установить бит по номеру [math](mask \mid (1\ \texttt{\lt \lt }\ x))[/math], снять бит по номеру [math](mask\ \&\ \sim(1\ \texttt{\lt \lt }\ x))[/math].

Битовые маски используются, например, при решении некоторых задач[1] динамического программирования.

Алгоритм Флойда

Основная статья: Алгоритм Флойда

Дерево Фенвика

Основная статья: Дерево Фенвика

Примечания

Источники информации