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

Материал из Викиконспекты
Перейти к: навигация, поиск
(Логические побитовые операции)
(Побитовые сдвиги)
Строка 48: Строка 48:
 
Операторы сдвига <tex>\ll</tex> и <tex>\gg</tex> сдвигают биты в переменной влево или вправо на указанное число. Сдвиг влево может применяться для умножения числа на два, сдвиг вправо — для деления.
 
Операторы сдвига <tex>\ll</tex> и <tex>\gg</tex> сдвигают биты в переменной влево или вправо на указанное число. Сдвиг влево может применяться для умножения числа на два, сдвиг вправо — для деления.
 
<code>
 
<code>
x = 7; //00000111
+
x = 7     <font color = green>//00000111</font>
 
+
x << 1   <font color = green>//00001110</font>
x << 1; //00001110
+
x << 5   <font color = green>//11000000</font>
 
+
x >> 2   <font color = green>//00110000</font>
x << 5; //11000000
 
 
 
x >> 2; //00110000
 
 
</code>
 
</code>
  
 
====Ограничения====
 
====Ограничения====
 
'''''C++ Visual Studio 15'''''
 
'''''C++ Visual Studio 15'''''
 +
 
Если выполняется сдвиг влево числа со знаком и при этом затрагивается бит знака, результат не определен. Результат сдвига вправо отрицательного числа со знаком зависит от реализации. Результат операции сдвига не определен, если число, на которое пользователь хочет сдвинуть биты имеет отрицательное значение или если оно больше или равно количеству битов в исходном числе.
 
Если выполняется сдвиг влево числа со знаком и при этом затрагивается бит знака, результат не определен. Результат сдвига вправо отрицательного числа со знаком зависит от реализации. Результат операции сдвига не определен, если число, на которое пользователь хочет сдвинуть биты имеет отрицательное значение или если оно больше или равно количеству битов в исходном числе.
 
<code>
 
<code>
#include <iostream>
+
     '''short''' x = 16384;    <font color = green>// 01000000 00000000</font>
#include <bitset>
+
     '''short''' y = x << 1;   <font color = green>// 10000000 00000000</font>
using namespace std;
+
                        <font color = green>// 16384 left-shifted by 1 = -32768</font>
 
 
int main() {
 
     short short1 = 16384;  
 
     bitset<16> bitset1{short2};
 
    cout << bitset1 << endl;  // 0100000000000000 <br/>
 
     short short3 = short1 << 1;
 
    bitset<16> bitset3{short3};  // 16384 left-shifted by 1 = -32768
 
    cout << bitset3 << endl;  // 100000000000000 <br/>
 
    short short4 = short1 << 14;
 
    bitset<16> bitset4{short4};  // 4 left-shifted by 14 = 0
 
    cout << bitset4 << endl;  // 000000000000000 
 
}
 
 
</code>
 
</code>
  
 
'''''Java'''''
 
'''''Java'''''
В языке программирования Java существует также оператор беззнакового битового сдвига вправо <tex>>>></tex>. При использовании этого оператора на освободившиеся позиции всегда устанавливаются <tex>0</tex>, тогда как при использовании <tex>>></tex> на освободившиеся позиции устанавливается бит знака.
+
 
 +
В языке программирования Java существует также оператор беззнакового битового сдвига вправо <tex>\ggg</tex>. При использовании этого оператора на освободившиеся позиции всегда устанавливаются <tex>0</tex>, тогда как при использовании <tex>\gg</tex> на освободившиеся позиции устанавливается бит знака.
 
При использовании битовых сдвигов есть некоторое отличие от целочисленного деления на <tex>2</tex>: если сдвигать отрицательное число вправо, то сначала это аналогично целочисленному делению на <tex>2</tex>, но когда останется <tex>-1</tex>, то при следующих сдвигах результат меняться не будет. То есть происходит округление не к нулю, как при целочисленном делении, а к <tex>-1</tex>.
 
При использовании битовых сдвигов есть некоторое отличие от целочисленного деления на <tex>2</tex>: если сдвигать отрицательное число вправо, то сначала это аналогично целочисленному делению на <tex>2</tex>, но когда останется <tex>-1</tex>, то при следующих сдвигах результат меняться не будет. То есть происходит округление не к нулю, как при целочисленном делении, а к <tex>-1</tex>.
  
Нельзя сдвинуть число на количество бит большее, чем разрядность операнда. При этом происходит неявное сокращение правого (кол-во бит) операнда.
+
Также нельзя сдвинуть число на количество бит большее, чем разрядность операнда. При этом происходит неявное сокращение правого (количество бит) операнда.
  
Примеры:
+
''Примеры:''
 +
<code>
 +
i = 1      <font color = green>//00000000 00000000 00000000 00000001 (1)</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>
 
<code>
i       00000000000000000000000000000001 (1) <br/>
+
i = -1      <font color = green>//11111111 11111111 11111111 11111111 (-1 / 4294967295)</font>
i<<29  00100000000000000000000000000000 (536870912) <br/>
+
i >>> 1    <font color = green>//01111111 11111111 11111111 11111111 (2147483647)</font>
i<<30  01000000000000000000000000000000 (1073741824) <br/>
+
i >>> 30    <font color = green>//00000000 00000000 00000000 00000011 (3)</font>
i<<31  10000000000000000000000000000000 (-2147483648/2147483648) <br/>
+
i >>> 31    <font color = green>//00000000 00000000 00000000 00000001 (1)</font>
i<<32  00000000000000000000000000000001 (1)
+
i >>> 32    <font color = green>//11111111 11111111 11111111 11111111 (-1 / 4294967295)</font>
 
</code>
 
</code>
 +
 
<code>
 
<code>
i       11111111111111111111111111111111 (-1/4294967295) <br/>
+
i = -192    <font color = green>//11111111 11111111 11111111 01000000 (-192 / 4294967104)</font>
i>>>1  01111111111111111111111111111111 (2147483647) <br/>
+
i >> 1      <font color = green>//11111111 11111111 11111111 10100000 (-96 / 4294967200)</font>
i>>>30  00000000000000000000000000000011 (3) <br/>
+
i >> 30    <font color = green>//11111111 11111111 11111111 11111111 (-1 / 4294967295)</font>
i>>>31  00000000000000000000000000000001 (1) <br/>
+
i >> 31    <font color = green>//11111111 11111111 11111111 11111111 (-1 / 4294967295)</font>
i>>>32  11111111111111111111111111111111 (-1/4294967295)
+
i >> 32    <font color = green>//11111111 11111111 11111111 01000000 (-192 / 4294967104)</font>
 
</code>
 
</code>
 +
 +
Арифметическое распространение в Java проводится перед операциями и гарантирует расширение каждого операнда по крайней мере до int (или, если один из операндов имеет больший тип, то до него). Расширение происходит знаково, ввиду чего результат может быть не таким, как ожидалось; при приведении типа к меньшему лишние байты отбрасываются.
 +
 +
''Примеры:''
 
<code>
 
<code>
i      11111111111111111111111101000000 (-192/4294967104) <br/>
+
'''byte''' b = -127  <font color = green>//10000001 (-127 / 129)</font>
i>>1    11111111111111111111111110100000 (-96/4294967200) <br/>
+
('''int''')b          <font color = green>//11111111 11111111 11111111 10000001 (-127 / 4294967169)</font>
i>>30  11111111111111111111111111111111 (-1/4294967295) <br/>
+
 
i>>31  11111111111111111111111111111111 (-1/4294967295) <br/>
+
'''int''' i = -127    <font color = green>//11111111 11111111 11111111 10000001 (-127 / 4294967169)</font>
i>>32  11111111111111111111111101000000 (-192/4294967104)
+
('''byte''')i         <font color = green>//10000001 (-127 / 129)</font>
</code>
 
  
Арифметическое распространение в Java проводится перед операциями и гарантирует расширение каждого операнда по крайней мере до int. Если же один из операндов long, то до long. Если сдвигаемая переменная по разрядности меньше, то результат будет не таким, как ожидалось. При приведении типов при расширении разрядности расширение происходит знаково. При сжатии разрядности просто отбрасываются лишние байты.
+
'''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>
  
<code>
+
  '''int''' i = -256    <font color = green>//11111111 11111111 11111111 00000000 (-256 / 4294967040)</font>
byte b = -127; <br/>
+
('''byte''')i         <font color = green>//00000000 (0)</font>
b      10000001 (-127/129) <br/>
 
(int)b 11111111 11111111 11111111 10000001 (-127/4294967169) <br/>
 
int i = -127; <br/>
 
i      11111111 11111111 11111111 10000001 (-127/4294967169) <br/>
 
(byte)i 10000001 (-127/129) <br/>
 
int i = 128; <br/>
 
i      00000000 00000000 00000000 10000000 (128) <br/>
 
(byte)i 10000000 (-128/128) <br/>
 
int i = 256; <br/>
 
i      00000000 00000000 00000001 00000000 (256) <br/>
 
(byte)i 00000000 (0) <br/>
 
int i = -256; <br/>
 
i      11111111 11111111 11111111 00000000 (-256/4294967040) <br/>
 
(byte)i 00000000 (0) <br/>
 
 
</code>
 
</code>
  

Версия 21:50, 6 марта 2016

ВНИМАНИЕ, СТАТЬЯ НАХОДИТСЯ В РАЗРАБОТКЕ

Побитовые операции (англ. bitwise operations) — операции, производимые над цепочками битов. Выделяют два типа побитовых операций: логические операции и побитовые сдвиги.

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

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

Битовые операторы И [math](AND, \&)[/math], ИЛИ [math](OR, \mid)[/math], НЕ [math](NOT, \sim)[/math] и исключающее ИЛИ [math](XOR, $\textasciicircum$)[/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]\ll[/math] и [math]\gg[/math] сдвигают биты в переменной влево или вправо на указанное число. Сдвиг влево может применяться для умножения числа на два, сдвиг вправо — для деления.

x = 7     //00000111
x << 1    //00001110
x << 5    //11000000
x >> 2    //00110000

Ограничения

C++ Visual Studio 15

Если выполняется сдвиг влево числа со знаком и при этом затрагивается бит знака, результат не определен. Результат сдвига вправо отрицательного числа со знаком зависит от реализации. Результат операции сдвига не определен, если число, на которое пользователь хочет сдвинуть биты имеет отрицательное значение или если оно больше или равно количеству битов в исходном числе.

   short x = 16384;     // 01000000 00000000
   short y = x << 1;    // 10000000 00000000
                        // 16384 left-shifted by 1 = -32768

Java

В языке программирования Java существует также оператор беззнакового битового сдвига вправо [math]\ggg[/math]. При использовании этого оператора на освободившиеся позиции всегда устанавливаются [math]0[/math], тогда как при использовании [math]\gg[/math] на освободившиеся позиции устанавливается бит знака. При использовании битовых сдвигов есть некоторое отличие от целочисленного деления на [math]2[/math]: если сдвигать отрицательное число вправо, то сначала это аналогично целочисленному делению на [math]2[/math], но когда останется [math]-1[/math], то при следующих сдвигах результат меняться не будет. То есть происходит округление не к нулю, как при целочисленном делении, а к [math]-1[/math].

Также нельзя сдвинуть число на количество бит большее, чем разрядность операнда. При этом происходит неявное сокращение правого (количество бит) операнда.

Примеры:

i = 1      //00000000 00000000 00000000 00000001 (1)
i << 29    //00100000 00000000 00000000 00000000 (536870912)
i << 30    //01000000 00000000 00000000 00000000 (1073741824)
i << 31    //10000000 00000000 00000000 00000000 (-2147483648 / 2147483648)
i << 32    //00000000 00000000 00000000 00000001 (1)

i = -1      //11111111 11111111 11111111 11111111 (-1 / 4294967295)
i >>> 1     //01111111 11111111 11111111 11111111 (2147483647)
i >>> 30    //00000000 00000000 00000000 00000011 (3)
i >>> 31    //00000000 00000000 00000000 00000001 (1)
i >>> 32    //11111111 11111111 11111111 11111111 (-1 / 4294967295)

i = -192    //11111111 11111111 11111111 01000000 (-192 / 4294967104)
i >> 1      //11111111 11111111 11111111 10100000 (-96 / 4294967200)
i >> 30     //11111111 11111111 11111111 11111111 (-1 / 4294967295)
i >> 31     //11111111 11111111 11111111 11111111 (-1 / 4294967295)
i >> 32     //11111111 11111111 11111111 01000000 (-192 / 4294967104)

Арифметическое распространение в Java проводится перед операциями и гарантирует расширение каждого операнда по крайней мере до int (или, если один из операндов имеет больший тип, то до него). Расширение происходит знаково, ввиду чего результат может быть не таким, как ожидалось; при приведении типа к меньшему лишние байты отбрасываются.

Примеры:

byte b = -127   //10000001 (-127 / 129)
(int)b          //11111111 11111111 11111111 10000001 (-127 / 4294967169)
int i = -127    //11111111 11111111 11111111 10000001 (-127 / 4294967169)
(byte)i         //10000001 (-127 / 129)
int i = 128     //00000000 00000000 00000000 10000000 (128)
(byte)i         //10000000 (-128 / 128)
int i = 256     //00000000 00000000 00000001 00000000 (256)
(byte)i         //00000000 (0)
int i = -256    //11111111 11111111 11111111 00000000 (-256 / 4294967040)
(byte)i         //00000000 (0)

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

  • Проверка на то, является ли число степенью двойки
    Если выражение [math](x\ \&\ (x - 1))[/math] равно нулю, то число [math]x[/math] является степенью двойки [math](x \not= 0)[/math].
  • Проверка на то, что в битовой записи числа нет двух единиц, идущих подряд
    Если выражение [math](x\ \&\ (x \ll 1))[/math] равно нулю, то в битовой записи числа [math]x[/math] нет двух единиц, идущих подряд.
  • Номер младшего единичного бита
    Число, полученное в результате операции [math]x\ \&\ (\sim x + 1)[/math] будет равно номеру младшего единичного бита в числе [math]x[/math].
  • Работа с битовыми масками
    Храним подмножества множества из [math]32, 64[/math] или [math]128[/math] фиксированных элементов. Значение каждого бита позволяет понять, включен элемент в множество или нет. Тогда легко сделать следующее: найти дополнение [math](\sim)[/math], пересечение [math](\&)[/math], объединение [math](\mid)[/math] множеств, установить бит по номеру [math](\mid 1 \ll x)[/math], снять бит по номеру [math](\& \sim(1 \ll x))[/math].
  • Определение знака числа

sign = (v != 0) | -(int)((unsigned int)((int)v) >> (sizeof(int) * CHAR_BIT - 1)); // Or, for more speed but less portability: sign = (v != 0) | (v >> (sizeof(int) * CHAR_BIT - 1)); // -1, 0, or +1 // Or, for portability, brevity, and (perhaps) speed: sign = (v > 0) - (v < 0); // -1, 0, or +1

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

Онлайн справочник программиста на С и С++

MSDN: Операторы сдвигов влево и вправо

Битовые сдвиги и приведения в Java: подводные камни