Побитовая операция и использование
Рассмотрим этот код:
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;
Надеюсь, это внесет свой вклад.