Вопрос-Ответ

Bitwise operation and usage

Побитовая операция и использование

Рассмотрим этот код:

x = 1        # 0001
x << 2 # Shift left 2 bits: 0100
# Result: 4

x | 2 # Bitwise OR: 0011
# Result: 3

x & 1 # Bitwise AND: 0001
# Result: 1

Я могу понимать арифметические операторы в Python (и других языках), но я никогда не понимал "побитовые" операторы достаточно хорошо. В приведенном выше примере (из книги по Python) я понимаю сдвиг влево, но не два других.

Кроме того, для чего на самом деле используются побитовые операторы? Я был бы признателен за несколько примеров.

Переведено автоматически
Ответ 1

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


  • AND равно 1, только если оба его входных параметра равны 1, в противном случае равно 0.

  • OR равно 1, если один или оба его входных параметра равны 1, в противном случае равно 0.

  • XOR равен 1, только если ровно один из его входных данных равен 1, в противном случае равен 0.

  • NOT равно 1, только если его входные данные равны 0, в противном случае это 0.

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

AND | 0 1     OR | 0 1     XOR | 0 1    NOT | 0 1
----+----- ---+---- ----+---- ----+----
0 | 0 0 0 | 0 1 0 | 0 1 | 1 0
1 | 0 1 1 | 1 1 1 | 1 0

Один из примеров: если вам нужны только младшие 4 бита целого числа, вы добавляете 15 (двоичный код 1111), так что:

    201: 1100 1001
AND 15: 0000 1111
------------------
IS 9 0000 1001

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

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

Итак, например:

1001 0101 >> 2 gives 0010 0101
1111 1111 << 4 gives 1111 0000

Обратите внимание, что сдвиг влево в Python необычен тем, что в нем не используется фиксированная ширина, при которой отбрасываются биты - в то время как многие языки используют фиксированную ширину в зависимости от типа данных, Python просто увеличивает ширину, чтобы учесть дополнительные биты. Чтобы получить поведение отбрасывания в Python, вы можете выполнить сдвиг влево побитовым and например, в 8-битном значении, сдвигающем влево четыре бита:

bits8 = (bits8 << 4) & 255

Имея это в виду, другой пример побитовых операторов: если у вас есть два 4-битных значения, которые вы хотите преобразовать в 8-битное, вы можете использовать все три ваших оператора (left-shift, and и or):

packed_val = ((val1 & 15) << 4) | (val2 & 15)

  • Операция & 15 гарантирует, что оба значения будут иметь только младшие 4 бита.

  • << 4 Это 4-битный сдвиг влево для перемещения val1 в верхние 4 бита 8-битного значения.

  • | просто объединяет эти два элемента вместе.

Если val1 равно 7, а val2 равно 4:

                val1            val2
==== ====
& 15 (and) xxxx-0111 xxxx-0100 & 15
<< 4 (left) 0111-0000 |
| |
+-------+-------+
|
| (or) 0111-0100
Ответ 2

Одно типичное использование:

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

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


  • Установите бит (где n - номер бита, а 0 - младший значащий бит):


    unsigned char a |= (1 << n);


  • Немного прояснить:


    unsigned char b &= ~(1 << n);


  • Немного переключить:


    unsigned char c ^= (1 << n);


  • Немного протестируем:


    unsigned char e = d & (1 << n);


Возьмем, к примеру, ваш список:

x | 2 используется для установки бита 1 из x равным 1

x & 1 используется для проверки, равен ли бит 0 из x 1 или 0

Ответ 3

для чего на самом деле используются побитовые операторы? Я был бы признателен за несколько примеров.


Одним из наиболее распространенных применений побитовых операций является синтаксический анализ шестнадцатеричных цветов.

Например, вот функция Python, которая принимает строку типа #FF09BE и возвращает кортеж из ее значений Red, Green и Blue.

def hexToRgb(value):
# Convert string to hexadecimal number (base 16)
num = (int(value.lstrip("#"), 16))

# Shift 16 bits to the right, and then binary AND to obtain 8 bits representing red
r = ((num >> 16) & 0xFF)

# Shift 8 bits to the right, and then binary AND to obtain 8 bits representing green
g = ((num >> 8) & 0xFF)

# Simply binary AND to obtain 8 bits representing blue
b = (num & 0xFF)
return (r, g, b)

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

Ответ 4

Я думаю, что вторая часть вопроса:


Кроме того, для чего на самом деле используются побитовые операторы? Я был бы признателен за несколько примеров.


Исправлено лишь частично. Вот мои два цента по этому поводу.

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

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


  • компьютерные сети;


  • телекоммуникационные приложения (сотовые телефоны, спутниковая связь и т.д.).


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

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

В качестве конкретного примера представьте, что кто-то предоставляет вам файл, содержащий необработанные данные, которые были получены непосредственно телекоммуникационным оборудованием. В этом случае, чтобы найти фреймы, вам нужно будет прочитать необработанные байты в файле и попытаться найти какие-то слова синхронизации, сканируя данные по крупицам. После определения слов синхронизации вам нужно будет получить фактические кадры и СДВИНУТЬ их, если необходимо (и это только начало истории), чтобы получить фактические данные, которые передаются.

Другое, совсем другое низкоуровневое семейство приложений - это когда вам нужно управлять оборудованием, используя некоторые (своего рода древние) порты, такие как параллельные и последовательные порты. Эти порты управляются путем установки нескольких байтов, и каждый бит этих байтов имеет определенное значение с точки зрения инструкций для этого порта (см., Например, http://en.wikipedia.org/wiki/Parallel_port). Если вы хотите создать программное обеспечение, которое что-то делает с этим оборудованием, вам понадобятся побитовые операции для перевода инструкций, которые вы хотите выполнить, в байты, понятные порту.

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

read = ((read ^ 0x80) >> 4) & 0x0f; 

Надеюсь, это внесет свой вклад.

python