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

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

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

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

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

НЕТ

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

НЕТ 0111 (7 десятичных) = 1000 (десятичное 8)
НЕ 10101011 (десятичное 171) = 01010100 (десятичное 84)

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

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

И

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

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

    0101 (десятичное 5) И 0011 (десятичный 3) = 0001 (десятичный 1)

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

    0011 (3 в десятичной системе) И 0010 (2 в десятичной системе) = 0010 (десятичный 2)

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

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

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

    0110 (десятичное 6) И 1011 (десятичное 11) = 0010 (десятичное 2)

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

    0110 (десятичное 6) И 0001 (десятичное 1) = 0000 (десятичное 0)

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

ИЛИ ЖЕ

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

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

   0101 (десятичное 5) ИЛИ 0011 (десятичный 3) = 0111 (десятичное 7)

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

   0010 (2 десятичных) ИЛИ 1000 (десятичное 8) = 1010 (десятичный 10)

XOR

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

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

    0101 (десятичное 5) XOR 0011 (3 в десятичной системе) = 0110 (десятичный 6)

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

    0010 (десятичный 2) XOR 1010 (десятичное 10) = 1000 (десятичный 8)

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

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

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

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

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

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

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

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

пqF0НИ1Xq2¬p34¬q5XOR6NAND7И8XNOR9q10Если / то11п12Тогда / если13ИЛИ ЖЕ14Т15
110000000011111111
100000111100001111
010011001100110011
000101010101010101
Побитовое
эквиваленты
0НЕТ
(p ИЛИ q)
(НЕ p)
И q
НЕТ
п
p И
(НЕ q)
НЕТ
q
p XOR qНЕТ
(р И q)
p И qНЕТ
(p XOR q)
q(НЕ p)
ИЛИ q
пp ИЛИ
(НЕ q)
p ИЛИ q1

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

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

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

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

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

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

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

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

В этом примере используется 8-битный регистр:

   00010111 (десятичное +23) ЛЕВЫЙ SHIFT = 00101110 (десятичный +46)
   10010111 (десятичное -23) СДВИГ ВПРАВО = 11001011 (десятичное -11)

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

   00010111 (десятичный +23) СДВИГ ВЛЕВО = 01011100 (десятичный +92)

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

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

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

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

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

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

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

Повернуть

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

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

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

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

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

Один вращать через перенос может имитировать логический или арифметический сдвиг одной позиции, предварительно установив флаг переноса. Например, если флаг переноса содержит 0, то x ПОВОРОТ ВПРАВО-ПЕРЕНОСИТЕ ПО ОДНОМУ является логическим сдвигом вправо, и если флаг переноса содержит копию знакового бита, то x ПОВОРОТ ВПРАВО-ПЕРЕНОСИТЕ ПО ОДНОМУ это арифметический сдвиг вправо. По этой причине некоторые микроконтроллеры, такие как low-end PIC просто иметь вращать и вращать через перенос, и не беспокойтесь об арифметических или логических инструкциях сдвига.

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

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

C-семья

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

Икс = у << 2;

назначает Икс результат смещения у слева на два бита, что эквивалентно умножению на четыре.

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

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

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

В семействе языков C отсутствует оператор поворота, но его можно синтезировать из операторов сдвига. Необходимо следить за тем, чтобы заявление было правильно сформировано, чтобы избежать неопределенное поведение и время атаки в ПО с требованиями безопасности.[6] Например, простая реализация, которая вращает влево 32-битное значение без знака Икс к п позиции просто:

uint32_t Икс = ..., п = ...;uint32_t у = (Икс << п) | (Икс >> (32 - п));

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

uint32_t Икс = ..., п = ...;uint32_t у = п ? (Икс << п) | (Икс >> (32 - п)) : Икс;

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

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

uint32_t Икс = ..., п = ...;uint32_t у = (Икс << п) | (Икс >> (-п & 31));

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

Ява

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

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

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

JavaScript

JavaScript использует побитовые операции для оценки каждого из двух или более место единиц до 1 или 0.[12]

Паскаль

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

Икс := у shl 2;

Другой

Приложения

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

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

c  0пока б  0    если (б и 1)  0        c  c + а    оставили сдвиг а к 1    верно сдвиг б к 1возвращаться c

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

пока а  0    c  б и а    б  б xor а    оставили сдвиг c к 1    а  cвозвращаться б

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

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

И

  • х & у = у & х
  • x & (y & z) = (x & y) & z
  • х & 0xFFFF = х[14]
  • х & 0 = 0
  • х & х = х

ИЛИ ЖЕ

  • х | y = y | Икс
  • х | (y | z) = (x | y) | z
  • х | 0 = х
  • х | 0xFFFF = 0xFFFF
  • х | х = х

НЕТ

  • ~ (~ х) = х

XOR

  • х ^ у = у ^ х
  • х ^ (у ^ г) = (х ^ у) ^ г
  • х ^ 0 = х
  • х ^ у ^ у = х
  • х ^ х = 0
  • х ^ 0xFFFF = ~ х

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

  • а ^ б = (а | б) & (~ а | ~ б)
  • a ^ b = (a & ~ b) | (~ а и б)

Другие

  • х | (х и у) = х
  • х & (х | у) = х
  • ~ (х | у) = ~ х & ~ у
  • ~ (x & y) = ~ x | ~ у
  • х | (y & z) = (x | y) & (x | z)
  • x & (y | z) = (x & y) | (x и z)
  • х & (у ^ г) = (х & у) ^ (х & z)
  • х + у = (х ^ у) + ((х и у) << 1)
  • х - у = ~ (~ х + у)

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

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

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

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

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

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

Рекомендации

  1. ^ «Блог о маломощных конструкциях CMicrotek». CMicrotek. Получено 2015-08-12.
  2. ^ а б JTC1 / SC22 / WG14 N843 "Язык программирования C", раздел 6.5.7
  3. ^ «Арифметические операторы - cppreference.com». en.cppreference.com. Получено 2016-07-06.
  4. ^ «INT13-C. Используйте побитовые операторы только для беззнаковых операндов». CERT: стандарты безопасного кодирования. Институт программной инженерии, Университет Карнеги-Меллона. Получено 2015-09-07.
  5. ^ «Оператор (Справочник по C #)». Microsoft. Получено 2013-07-14.
  6. ^ а б «Почти постоянное время вращения, что не нарушает стандартов?». Сеть обмена стеком. Получено 2015-08-12.
  7. ^ «Плохая оптимизация переносимой идиомы поворота». Проект GNU GCC. Получено 2015-08-11.
  8. ^ "Круговой поворот, не нарушающий стандарт C / C ++?". Форумы разработчиков Intel. Получено 2015-08-12.
  9. ^ а б "Константа не передана во встроенную сборку, в результате" ограничение 'I' ожидает целочисленное выражение константы "". LLVM проект. Получено 2015-08-11.
  10. ^ Спецификация языка Java, раздел 15.19. Операторы смены
  11. ^ а б «Глава 15. Выражения». oracle.com.
  12. ^ «Побитовый JavaScript». W3Schools.com.
  13. ^ «Синтез арифметических операций с использованием трюков со сдвигом бит». Bisqwit.iki.fi. 2014-02-15. Получено 2014-03-08.
  14. ^ В этой статье 0xFFFF означает, что все биты в вашем типе данных должны быть установлены в 1. Точное количество битов зависит от ширины типа данных.
  15. ^ - здесь отрицание, а не вычитание
  16. ^ - здесь вычитание, а не отрицание

внешняя ссылка