Побитовая операция - Bitwise operation

В компьютерном программировании , A побитовая операция работает на битовую строку , в битовом массиве или двоичную цифре (рассматриваемой в качестве битовой строки) на уровне отдельных его бит . Это быстрое и простое действие, базовое для арифметических операций более высокого уровня и непосредственно поддерживаемое процессором . Большинство побитовых операций представлены как инструкции с двумя операндами, в которых результат заменяет один из входных операндов.

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

Побитовые операторы

В пояснениях ниже любое указание положения бита отсчитывается с правой (наименее значимой) стороны, продвигаясь влево. Например, двоичное значение 0001 (десятичная 1) имеет нули в каждой позиции, кроме первой (то есть самой правой).

НЕТ

Побитовое НЕ , или дополнение , является унарной операцией , которая выполняет логическое отрицание на каждый бит, формируя обратный код данного двоичного значения. Биты, равные 0, становятся 1, а те, которые равны 1, становятся 0. Например:

NOT 0111  (decimal 7)
  = 1000  (decimal 8)
NOT 10101011  (decimal 171)
  = 01010100  (decimal 84)

Поразрядное дополнение равно двум дополнительным значениям минус один. Если используется арифметика с дополнением до двух, то NOT x = -x − 1.

Для беззнаковых целых чисел побитовое дополнение числа является «зеркальным отражением» числа в средней точке диапазона беззнаковых целых чисел. Например, для 8-битных целых чисел без знака NOT x = 255 - x, которые можно визуализировать на графике в виде нисходящей линии, которая эффективно «переворачивает» увеличивающийся диапазон от 0 до 255 в убывающий диапазон от 255 до 0. Простой, но наглядный пример использования заключается в инвертировании изображения в градациях серого, где каждый пиксель хранится как целое число без знака.

А ТАКЖЕ

Побитовое И 4-битных целых чисел

Побитовое И это бинарная операция , которая принимает два равной длины двоичных представлений и выполняет логическую операцию над каждой парой соответствующих битов, что эквивалентно умножению их. Таким образом, если оба бита в сравниваемой позиции равны 1, бит в результирующем двоичном представлении равен 1 (1 × 1 = 1); в противном случае результат равен 0 (1 × 0 = 0 и 0 × 0 = 0). Например:

    0101 (decimal 5)
AND 0011 (decimal 3)
  = 0001 (decimal 1)

Операция может быть использована для определения , является ли конкретный бит установлен (1) или ясно (0). Например, учитывая битовый шаблон 0011 (десятичное число 3), чтобы определить, установлен ли второй бит, мы используем побитовое И с битовым шаблоном, содержащим 1 только во втором бите:

    0011 (decimal 3)
AND 0010 (decimal 2)
  = 0010 (decimal 2)

Поскольку результат 0010 не равен нулю, мы знаем, что второй бит в исходном шаблоне был установлен. Это часто называется битовой маскировкой . (По аналогии, использование малярной ленты закрывает или маски , части, которые не следует изменять, или части, которые не представляют интереса. В этом случае значения 0 маскируют биты, которые не представляют интереса.)

Поразрядное И может использоваться для очистки выбранных битов (или флагов ) регистра, в котором каждый бит представляет отдельное логическое состояние. Этот метод является эффективным способом хранения ряда логических значений с использованием как можно меньшего объема памяти.

Например, 0110 (десятичное число 6) можно рассматривать как набор из четырех флагов, где первый и четвертый флаги сняты (0), а второй и третий флаги установлены (1). Третий флаг можно сбросить с помощью побитового И с шаблоном, который имеет ноль только в третьем бите:

    0110 (decimal 6)
AND 1011 (decimal 11)
  = 0010 (decimal 2)

Благодаря этому свойству становится легко проверить четность двоичного числа, проверив значение самого младшего бита. Используя приведенный выше пример:

    0110 (decimal 6)
AND 0001 (decimal 1)
  = 0000 (decimal 0)

Поскольку 6 И 1 равно нулю, 6 делится на два и, следовательно, четно.

ИЛИ

Побитовое ИЛИ 4-битных целых чисел

Побитовое ИЛИ представляет собой бинарную операцию , которая принимает два битовых комбинаций равной длины и производит логическое включающее ИЛИ операцию на каждой паре соответствующих битов. Результатом в каждой позиции является 0, если оба бита равны 0, в противном случае результат равен 1. Например:

   0101 (decimal 5)
OR 0011 (decimal 3)
 = 0111 (decimal 7)

Поразрядное ИЛИ может использоваться для установки в 1 выбранных битов регистра, описанного выше. Например, четвертый бит 0010 (десятичное 2) может быть установлен путем выполнения побитового ИЛИ с шаблоном только с установленным четвертым битом:

   0010 (decimal 2)
OR 1000 (decimal 8)
 = 1010 (decimal 10)

XOR

Побитовое исключающее ИЛИ 4-битных целых чисел

Побитовое исключающее ИЛИ является бинарная операция , которая принимает два битовых комбинаций равной длины и выполняет логическое исключающее ИЛИ операции на каждой паре соответствующих битов. Результатом в каждой позиции будет 1, если только один из битов равен 1, но будет 0, если оба равны 0 или оба равны 1. В этом случае мы выполняем сравнение двух битов, равное 1, если два бита различны, и 0 если они такие же. Например:

    0101 (decimal 5)
XOR 0011 (decimal 3)
  = 0110 (decimal 6)

Побитовое исключающее ИЛИ можно использовать для инвертирования выбранных битов в регистре (также называемого переключением или переключением). Любой бит можно переключить с помощью XOR с 1. Например, учитывая битовый шаблон 0010 (десятичное 2), второй и четвертый биты могут переключаться с помощью побитового XOR с битовым шаблоном, содержащим 1 во второй и четвертой позициях:

    0010 (decimal 2)
XOR 1010 (decimal 10)
  = 1000 (decimal 8)

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

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

Если набор битовых строк фиксированной длины n (т. Е. Машинных слов ) рассматривается как n -мерное векторное пространство над полем , то сложение векторов соответствует побитовой операции XOR.

Математические эквиваленты

Предполагая , что для неотрицательных целых чисел побитовые операции можно записать следующим образом:

Таблица истинности для всех бинарных логических операторов

Есть 16 возможных функций истинности двух двоичных переменных ; это определяет таблицу истинности .

Вот побитовые эквивалентные операции двух битов P и Q:

п q F 0 NOR 1 Xq 2 ¬p 3 4 ¬q 5 XOR 6 NAND 7 И 8 XNOR 9 q 10 Если / то 11 стр. 12 Тогда / если 13 ИЛИ 14 Т 15
1 1 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1
1 0 0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 1
0 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1
0 0 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1
Побитовые
эквиваленты
0 НЕ
(p ИЛИ q)
(НЕ p)
И q
НЕ
п
p И
(НЕ q)
НЕ
q
p XOR q НЕ
(p И q)
p И q НЕ
(p XOR q)
q (НЕ p)
ИЛИ q
п p ИЛИ
(НЕ q)
p ИЛИ q 1

Битовые сдвиги

В битовых сдвигах иногда рассматриваются битовые операциями, поскольку они относятся к значению как последовательность бит , а не как числовая величина. В этих операциях цифры перемещаются или сдвигаются влево или вправо. Регистры в процессоре компьютера имеют фиксированную ширину, поэтому некоторые биты будут «сдвинуты» из регистра на одном конце, в то время как такое же количество битов «сдвинется» с другого конца; Различия между операторами сдвига битов заключаются в том, как они определяют значения сдвинутых битов.

Битовая адресация

Если ширина регистра (часто 32 или даже 64) больше, чем количество бит (обычно 8) наименьшего адресуемого блока (атомарного элемента), часто называемого байтом, операции сдвига вызывают схему адресации от байтов к биты. Таким образом, ориентации «влево» и «вправо» взяты из стандартной записи чисел в нотации разряда, так что сдвиг влево увеличивается, а сдвиг вправо уменьшает значение числа - если сначала считываются левые цифры, это составляет прямую последовательность байтов . Не обращая внимания на граничные эффекты на обоих концах регистра, операции арифметического и логического сдвига ведут себя одинаково, а сдвиг на 8-битные позиции переносит битовый шаблон на 1-байтовую позицию следующим образом:

сдвиг влево на 8 позиций увеличивает адрес байта на 1
  • Порядок с прямым порядком байтов:
сдвиг вправо на 8 позиций уменьшает адрес байта на 1
сдвиг влево на 8 позиций уменьшает адрес байта на 1
  • Порядок с прямым порядком байтов:
сдвиг вправо на 8 позиций увеличивает адрес байта на 1

Арифметический сдвиг

Левый арифметический сдвиг
Правый арифметический сдвиг

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

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

   00010111 (decimal +23) LEFT-SHIFT
=  00101110 (decimal +46)
   10010111 (decimal −105) RIGHT-SHIFT
=  11001011 (decimal −53)

В первом случае крайний левый разряд был сдвинут за конец регистра, а новый 0 был сдвинут в крайнее правое положение. Во втором случае крайняя правая 1 была сдвинута (возможно, во флаг переноса ), а новая 1 была скопирована в крайнюю левую позицию с сохранением знака числа. Несколько смен иногда сокращаются до одной смены на некоторое количество цифр. Например:

   00010111 (decimal +23) LEFT-SHIFT-BY-TWO
=  01011100 (decimal +92)

Левый арифметический сдвиг на n эквивалентен умножению на 2 n (при условии, что значение не переполняется ), в то время как правый арифметический сдвиг на n значения с дополнением до двух эквивалентен взятию минимального уровня деления на 2 n . Если двоичное число трактуется как дополнение до единиц , то та же операция сдвига вправо приводит к делению на 2 n и округлению в сторону нуля .

Логический сдвиг

Левый логический сдвиг
Правый логический сдвиг

При логическом сдвиге нули сдвигаются, чтобы заменить отброшенные биты. Следовательно, логический и арифметический сдвиги влево абсолютно одинаковы.

Однако, поскольку логический сдвиг вправо вставляет биты значения 0 в самый старший бит, вместо копирования знакового бита, он идеально подходит для двоичных чисел без знака, в то время как арифметический сдвиг вправо идеально подходит для двоичных чисел с дополнением до двух со знаком .

Круговой сдвиг

Другой формой сдвига является круговой сдвиг , побитовое вращение или битовое вращение .

Повернуть

Левый круговой сдвиг или поворот
Правый круговой сдвиг или поворот

В этой операции, иногда называемой вращением без переноса , биты «вращаются», как если бы левый и правый концы регистра были соединены. Значение, которое сдвигается вправо во время сдвига влево, соответствует тому значению, которое было сдвинуто влево, и наоборот для операции сдвига вправо. Это полезно, если необходимо сохранить все существующие биты, и часто используется в цифровой криптографии .

Повернуть через перенос

Левое вращение через перенос
Повернуть вправо через перенос

Поворот через перенос - это вариант операции поворота, где бит, который сдвигается (на любом конце), является старым значением флага переноса, а бит, который сдвигается (на другом конце), становится новым значением флаг переноса.

Одиночный поворот через перенос может имитировать логический или арифметический сдвиг одной позиции, предварительно установив флаг переноса. Например, если флаг переноса содержит 0, то x RIGHT-ROTATE-THROUGH-CARRY-BY-ONEэто логический сдвиг вправо, а если флаг переноса содержит копию знакового бита, то x RIGHT-ROTATE-THROUGH-CARRY-BY-ONEэто арифметический сдвиг вправо. По этой причине некоторые микроконтроллеры, такие как младшие PIC, просто имеют вращение и вращение посредством переноса и не беспокоятся об арифметических или логических инструкциях сдвига.

Чередование сквозного переноса особенно полезно при выполнении сдвигов над числами, превышающими собственный размер слова процессора , потому что, если большое число хранится в двух регистрах, бит, сдвинутый с одного конца первого регистра, должен приходить на другом конце секунда. При сквозном переносе этот бит «сохраняется» во флаге переноса во время первой смены и готов к замене во время второй смены без какой-либо дополнительной подготовки.

На языках высокого уровня

C-семья

В языках C-семейства операторы логического сдвига - " <<" для сдвига влево и " >>" для сдвига вправо. Число мест для сдвига указывается как второй аргумент оператора. Например,

x = y << 2;

присваивает xрезультат сдвига yвлево на два бита, что эквивалентно умножению на четыре.

Сдвиги могут привести к поведению, определяемому реализацией, или к неопределенному поведению , поэтому при их использовании необходимо соблюдать осторожность. Результатом сдвига на бит, превышающий или равный размеру слова, является неопределенное поведение в C и C ++. Сдвиг вправо отрицательного значения определяется реализацией и не рекомендуется хорошей практикой кодирования; результат сдвига влево значения со знаком не определен, если результат не может быть представлен в типе результата.

В C # сдвиг вправо - это арифметический сдвиг, когда первый операнд имеет тип int или long. Если первый операнд имеет тип uint или ulong, сдвиг вправо является логическим сдвигом.

Круговые сдвиги

В семействе языков C отсутствует оператор поворота (хотя C ++ 20 предоставляет std::rotlи std::rotr), но его можно синтезировать из операторов сдвига. Необходимо позаботиться о том, чтобы заявление было правильно сформировано, чтобы избежать атак неопределенного поведения и времени в программном обеспечении с требованиями безопасности. Например, простая реализация, которая вращает 32-битное беззнаковое значение влево xпо nпозициям, просто:

uint32_t x = ..., n = ...;
uint32_t y = (x << n) | (x >> (32 - n));

Однако сдвиг по 0битам приводит к неопределенному поведению в правом выражении, (x >> (32 - n))потому что 32 - 0is 32и 32is за пределами диапазона [0 - 31]включительно. Вторая попытка может привести к:

uint32_t x = ..., n = ...;
uint32_t y = n ? (x << n) | (x >> (32 - n)) : x;

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

Чтобы избежать неопределенного поведения и ветвей в GCC и Clang, рекомендуется следующее. Шаблон распознается многими компиляторами, и компилятор выдаст единственную инструкцию поворота:

uint32_t x = ..., n = ...;
uint32_t y = (x << n) | (x >> (-n & 31));

Существуют также специфичные для компилятора встроенные функции, реализующие циклические сдвиги , такие как _rotl8, _rotl16 , _rotr8, _rotr16 в Microsoft Visual C ++ . Clang предоставляет некоторые встроенные функции ротации для совместимости с Microsoft, которые страдают вышеуказанными проблемами. GCC не предлагает встроенных функций ротации. Intel также предоставляет встроенные функции x86 .

Джава

В Java все целочисленные типы подписаны, поэтому операторы " <<" и " >>" выполняют арифметические сдвиги. Java добавляет оператор " >>>" для выполнения логического сдвига вправо, но поскольку логические и арифметические операции сдвига влево идентичны для целого числа со знаком, <<<в Java нет оператора " ".

Подробнее об операторах сдвига в Java:

  • Операторы <<(сдвиг влево), >>(сдвиг вправо со знаком) и >>>(сдвиг вправо без знака) называются операторами сдвига .
  • Тип выражения сдвига - это расширенный тип левого операнда. Например, aByte >>> 2эквивалентно .((int) aByte) >>> 2
  • Если повышенный тип левого операнда - int, только пять младших битов правого операнда используются в качестве расстояния сдвига. Это как если бы правый операнд был подвергнут поразрядному логическому оператору AND & со значением маски 0x1f (0b11111). Таким образом, фактически используемое расстояние сдвига всегда находится в диапазоне от 0 до 31 включительно.
  • Если расширенный тип левого операнда длинный, то в качестве расстояния сдвига используются только шесть младших битов правого операнда. Это как если бы правый операнд был подвергнут поразрядному логическому оператору AND & со значением маски 0x3f (0b111111). Таким образом, фактически используемое расстояние сдвига всегда находится в диапазоне от 0 до 63 включительно.
  • Значение n >>> sравно n сдвинутым вправо s битовым позициям с нулевым расширением.
  • В битовых операциях и операциях сдвига тип byteнеявно преобразуется в int. Если значение байта отрицательное, старший бит равен единице, тогда единицы используются для заполнения дополнительных байтов в int. Так приведет к .byte b1 = -5; int i = b1 | 0x0200;i == -5

JavaScript

JavaScript использует поразрядные операции для оценки каждой из двух или более единиц, помещаемых в 1 или 0.

Паскаль

В Паскале, а также во всех его диалектах (таких как Object Pascal и Standard Pascal ) логические операторы сдвига влево и вправо - это " shl" и " shr" соответственно. Даже для целых чисел shrсо знаком ведет себя как логический сдвиг и не копирует знаковый бит. Количество мест, которые нужно сместить, указывается в качестве второго аргумента. Например, следующее присваивает x результат сдвига y влево на два бита:

x := y shl 2;

Другой

Приложения

Поразрядные операции необходимы, в частности, в программировании нижнего уровня, таком как драйверы устройств, графика нижнего уровня, сборка пакетов протокола связи и декодирование.

Хотя машины часто имеют эффективные встроенные инструкции для выполнения арифметических и логических операций, все эти операции могут выполняться путем комбинирования побитовых операторов и проверки нуля различными способами. Например, вот реализация псевдокода древнеегипетского умножения, показывающая, как умножить два произвольных целых числа aи b( aбольше b), используя только битовые сдвиги и сложение:

c  0
while b  0
    if (b and 1)  0
        c  c + a
    left shift a by 1
    right shift b by 1
return c

Другой примером является реализацией псевдокода Кроме того, показано , как вычислить сумму двух чисел aи с bпомощью операторов битовых и нулевого тестирования:

while a  0
    c  b and a
    b  b xor a
    left shift c by 1
    a  c
return b

Булева алгебра

Иногда бывает полезно упростить сложные выражения, состоящие из побитовых операций. Например, при написании компиляторов. Цель компилятора - перевести язык программирования высокого уровня в максимально эффективный машинный код . Булева алгебра используется для упрощения сложных побитовых выражений.

А ТАКЖЕ

  • x & y = y & x
  • x & (y & z) = (x & y) & z
  • x & 0xFFFF = x
  • x & 0 = 0
  • x & x = x

ИЛИ

  • x | y = y | x
  • x | (y | z) = (x | y) | z
  • x | 0 = x
  • x | 0xFFFF = 0xFFFF
  • x | x = x

НЕТ

  • ~(~x) = x

XOR

  • x ^ y = y ^ x
  • x ^ (y ^ z) = (x ^ y) ^ z
  • x ^ 0 = x
  • x ^ y ^ y = x
  • x ^ x = 0
  • x ^ 0xFFFF = ~x

Кроме того, XOR может быть составлен с использованием 3 основных операций (И, ИЛИ, НЕ).

  • a ^ b = (a | b) & (~a | ~b)
  • a ^ b = (a & ~b) | (~a & b)

Другие

  • x | (x & y) = x
  • x & (x | y) = x
  • ~(x | y) = ~x & ~y
  • ~(x & y) = ~x | ~y
  • x | (y & z) = (x | y) & (x | z)
  • x & (y | z) = (x & y) | (x & z)
  • x & (y ^ z) = (x & y) ^ (x & z)
  • x + y = (x ^ y) + ((x & y) << 1)
  • x - y = ~(~x + y)

Обращения и решение уравнений

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

  • Имеет инверсию
    • НЕТ
    • XOR
    • Повернуть налево
    • Повернуть вправо
  • Нет обратного
    • А ТАКЖЕ
    • ИЛИ
    • Сдвиг влево
    • Сдвиг вправо

Порядок действий

Сначала выполняются операции, расположенные вверху этого списка. См. Основную статью для более полного списка.

  • ( )
  • ~ -
  • * / %
  • + -
  • << >>
  • &
  • ^
  • |

Смотрите также

использованная литература

внешние ссылки