Побітове функціонування та використання


102

Розглянемо цей код:

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) я розумію зсув вліво, але не два інших.

Також для чого насправді використовуються побітові оператори? Я б оцінив кілька прикладів.


11
Це може становити інтерес: stackoverflow.com/questions/276706/what-are-bitwise-operators
outis

Відповіді:


163

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

  • ANDє 1, лише якщо обидва його входи дорівнюють 1, інакше це 0.
  • ORє 1, якщо один або обидва його входи дорівнюють 1, інакше це 0.
  • XORє 1, лише якщо рівно один з його входів 1, інакше це 0.
  • NOT є 1, лише якщо його вхід дорівнює 0, інакше це 0.

Їх часто можна найкраще показати як таблиці правдивості. Можливості введення знаходяться вгорі та зліва, отриманий біт - це одне з чотирьох (два у випадку НЕ, оскільки він має лише один вхід) значення, показані на перетині входів.

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 біти.
  • Це << 44-бітний зсув вліво для переміщення val1у перші чотири біти 8-бітного значення.
  • |Просто об'єднує ці два разом.

Якщо val17 і val2дорівнює 4:

                val1            val2
                ====            ====
 & 15 (and)   xxxx-0111       xxxx-0100  & 15
 << 4 (left)  0111-0000           |
                  |               |
                  +-------+-------+
                          |
| (or)                0111-0100

43

Одне типове використання:

| використовується для встановлення певного біта на 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 від x1

x & 1використовується для тестування, якщо біт 0 xдорівнює 1 або 0


38

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

Одне з найпоширеніших застосувань побітових операцій - для розбору шістнадцяткових кольорів.

Наприклад, ось функція Python, яка приймає String як #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)

Я знаю, що є більш ефективні способи досягти цього, але я вважаю, що це дійсно стислий приклад, що ілюструє як зміщення, так і розрядні булі операції.


14

Я думаю, що друга частина питання:

Також для чого насправді використовуються побітові оператори? Я б оцінив кілька прикладів.

Було розглянуто лише частково. Це два мої центи з цього приводу.

Бітові операції в мовах програмування відіграють основну роль при роботі з багатьма програмами. Практично всі обчислення низького рівня повинні бути виконані з використанням цього виду операцій.

У всіх програмах, яким потрібно надсилати дані між двома вузлами, такими як:

  • комп'ютерні мережі;

  • телекомунікаційні програми (стільникові телефони, супутниковий зв’язок тощо).

На рівні нижнього рівня зв'язку дані зазвичай надсилаються у так званих кадрах . Кадри - це лише рядки байтів, які надсилаються через фізичний канал. Ці кадри зазвичай містять фактичні дані плюс деякі інші поля (закодовані в байтах), які є частиною того, що називається заголовком . Заголовок зазвичай містить байти, які кодують певну інформацію, що стосується стану зв'язку (наприклад, з прапорами (бітами)), лічильниками кадрів, кодами виправлення та виявлення помилок тощо. Для отримання переданих даних у кадр та для побудови кадри для надсилання даних, вам знадобляться певні побізні операції.

Як правило, при роботі з подібними програмами доступний API, тому вам не доведеться мати справу з усіма цими деталями. Наприклад, усі сучасні мови програмування надають бібліотеки для підключення сокетів, тому вам фактично не потрібно будувати кадри зв'язку TCP / IP. Але подумайте про хороших людей, які програмували ці API для вас, вони мусили мати справу із побудовою кадру; використовуючи всі види бітових операцій, щоб переходити назад і назад від низького рівня до комунікації вищого рівня.

Як конкретний приклад, уявіть, що хтось із них пропонує вам файл, який містить необроблені дані, які були захоплені безпосередньо апаратними засобами зв'язку. У цьому випадку, щоб знайти кадри, вам потрібно буде прочитати необроблені байти у файлі та спробувати знайти якісь слова синхронізації, скануючи дані побіжно. Визначивши слова синхронізації, вам потрібно буде отримати фактичні кадри та при необхідності SHIFT їх (і це лише початок історії), щоб отримати фактичні дані, які передаються.

Ще одна дуже відрізняється сімейство застосувань низького рівня - це коли вам потрібно керувати обладнанням за допомогою деяких (різновидів древніх) портів, таких як паралельний і послідовний порти. Цими портами керується встановленням декількох байтів, і кожен біт цього байта має конкретне значення, з точки зору інструкцій, для цього порту (див., Наприклад, http://en.wikipedia.org/wiki/Parallel_port ). Якщо ви хочете створити програмне забезпечення, яке щось робить із цим обладнанням, вам знадобляться побітні операції для перекладу інструкцій, які ви хочете виконати, до байтів, які розуміє порт.

Наприклад, якщо до паралельного порту підключені фізичні кнопки для управління деяким іншим пристроєм, це рядок коду, який ви можете знайти в програмному забезпеченні:

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

Сподіваюсь, це сприяє.


Я б додав en.wikipedia.org/wiki/Bit_banging в якості іншого напряму, щоб вивчити, чи читати про паралельні та послідовні порти як приклад, коли побітові операції можуть бути корисними.
Дан

6

Я сподіваюся, що це пояснює ці два:

x | 2

0001 //x
0010 //2

0011 //result = 3

x & 1

0001 //x
0001 //1

0001 //result = 1

4
На жаль, намагався бути найшвидшим пістолетом на заході .... виявився ідіот, який навіть не знає бінарного для двох :( Виправлено це.
Amarghosh

1
x & 1не ілюструє ефект так добре, як x & 2би.
dansalmo

5

Подумай 0 як хибне, а 1 - як істинне Тоді побітові та (&) та / або (|) працюють так само, як регулярні та і, за винятком того, що вони виконують всі біти у значенні одразу. Як правило, ви побачите, що вони використовуються для прапорів, якщо у вас є 30 параметрів, які можна встановити (скажімо, як стилі малювання у вікні), вам не потрібно передавати 30 окремих булевих значень, щоб встановити або вимкнути кожен з них, щоб ви використовували | об'єднати параметри в одне значення, а потім використовувати &, щоб перевірити, чи встановлено кожен параметр. Цей стиль передачі прапора широко використовується у OpenGL. Оскільки кожен біт є окремим прапором, ви отримуєте значення прапорців на потужність двох (ака чисел, у яких встановлено лише один біт) 1 (2 ^ 0) 2 (2 ^ 1) 4 (2 ^ 2) 8 (2 ^ 3) потужність двох повідомляє, який біт встановлюється, якщо прапор увімкнено.

Також зауважте 2 = 10, тому x | 2 дорівнює 110 (6), а не 111 (7) Якщо жоден з бітів не перекривається (що вірно в цьому випадку) | діє як доповнення.


5

Я не бачив цього вище, але ви також побачите, що деякі люди використовують зсув вліво і вправо для арифметичних операцій. Зсув вліво на x еквівалентно множенню на 2 ^ х (доки він не переповнює), а правий зсув еквівалентний діленню на 2 ^ х.

Останнім часом я бачив людей, які використовують x << 1 та x >> 1 для подвоєння та скорочення вдвічі, хоча я не впевнений, чи намагаються вони бути розумними чи справді є явна перевага перед звичайними операторами.


1
Я не знаю про python, але в мовах нижчого рівня, таких як C або навіть більш низька - збірка, побітове зміщення є набагато ефективнішим. Щоб побачити різницю, ви можете написати програму на C, роблячи це в кожному разі, і просто компілювати до асемблерного коду (або, якщо ви знаєте збірний янг, ви б це вже знали :)). Дивіться різницю в кількості інструкцій.
0xc0de

2
Мій аргумент проти використання операторів бітового зсуву полягав би в тому, що більшість сучасних компіляторів, ймовірно, оптимізують арифметичні операції, тому розумність в кращому випадку суперечить або в гіршому випадку бореться з компілятором. У мене немає досвіду роботи з C, компіляторами або процесорами, тому не припускаю, що я правильно. :)
P. Stallworth

Це повинно бути вище. Мені довелося мати справу з деяким кодом, який саме таким чином використовував побітовий оператор, і ця відповідь допомогла мені з'ясувати речі.
Філіпп Огер

4

Набори

Набори можна комбінувати за допомогою математичних операцій.

  • Оператор об'єднання |об'єднує два набори, щоб сформувати новий, який містить елементи в будь-якому.
  • Оператор перехрестя &отримує елементи лише в обох.
  • Оператор різниці -отримує елементи у першому наборі, а не у другому.
  • Оператор симетричної різниці ^отримує елементи в будь-якому наборі, але не в обох.

Спробуйте самі:

first = {1, 2, 3, 4, 5, 6}
second = {4, 5, 6, 7, 8, 9}

print(first | second)

print(first & second)

print(first - second)

print(second - first)

print(first ^ second)

Результат:

{1, 2, 3, 4, 5, 6, 7, 8, 9}

{4, 5, 6}

{1, 2, 3}

{8, 9, 7}

{1, 2, 3, 7, 8, 9}

Ця відповідь абсолютно не пов'язана з питанням і, здається, була скопійована та вставлена ​​з іншого місця.
доктофред

Питання задає "Для чого насправді використовуються побітові оператори?". Ця відповідь дає менш відоме, але дуже корисне використання бітових операторів.
Taegyung

3

Цей приклад покаже вам операції для всіх чотирьох 2 бітних значень:

10 | 12

1010 #decimal 10
1100 #decimal 12

1110 #result = 14

10 & 12

1010 #decimal 10
1100 #decimal 12

1000 #result = 8

Ось один із прикладів використання:

x = raw_input('Enter a number:')
print 'x is %s.' % ('even', 'odd')[x&1]

2

Іншим поширеним випадком використання є маніпулювання / тестування дозволів на файли. Див. Модуль статистики Python: http://docs.python.org/library/stat.html .

Наприклад, щоб порівняти дозволи файлів із потрібним набором дозволів, ви можете зробити щось на кшталт:

import os
import stat

#Get the actual mode of a file
mode = os.stat('file.txt').st_mode

#File should be a regular file, readable and writable by its owner
#Each permission value has a single 'on' bit.  Use bitwise or to combine 
#them.
desired_mode = stat.S_IFREG|stat.S_IRUSR|stat.S_IWUSR

#check for exact match:
mode == desired_mode
#check for at least one bit matching:
bool(mode & desired_mode)
#check for at least one bit 'on' in one, and not in the other:
bool(mode ^ desired_mode)
#check that all bits from desired_mode are set in mode, but I don't care about 
# other bits.
not bool((mode^desired_mode)&desired_mode)

Я ставлю результати як булеві, тому що я дбаю лише про правду чи неправду, але було б корисним вправою роздрукувати значення bin () для кожного.


1
Ви помиляєтесь в останньому прикладі. Ось як це повинно виглядати так: not bool((mode ^ desired_mode) & 0777). Або (легше зрозуміти) not (mode & 0777) ^ desired_mode == 0. І залишить лише цікаві біти, XOR перевірить, що всі бажані біти встановлені. Явне == 0порівняння є більш значущим, ніж bool().
Вадим Фінт

Я не думаю, що це стосується файлових операцій. Наприклад, в PyQt ви робите щось подібне для setWindowFlags. Приклад: setWindowFlags(SplashScreen | WindowStaysOnTopHint). Я все ще вважаю це заплутаним, оскільки це здається перемиканням, яке ви встановлюєте на «увімкнено», тому воно здається більш інтуїтивним «і» у такому випадку.
eric

2

Бітові зображення цілих чисел часто використовуються в наукових обчисленнях для представлення масивів правдиво-хибної інформації, оскільки побітова операція набагато швидша, ніж ітерація через масив булевих. (Мови вищого рівня можуть використовувати ідею бітового масиву.)

Приємним і досить простим прикладом цього є загальне рішення гри Німа. Подивіться на код Python на сторінці Вікіпедії . Він широко використовує побітовое виключає або, ^.


1

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

import numpy as np
a=np.array([1.2, 2.3, 3.4])
np.where((a>2) and (a<3))      
#Result: Value Error
np.where((a>2) & (a<3))
#Result: (array([1]),)

1

я не бачив, як це згадувалося. Цей приклад покаже вам (-) десяткову операцію для двох бітних значень: AB (лише якщо A містить B)

ця операція потрібна, коли ми тримаємо в нашій програмі дієслово, яке представляє біти. іноді нам потрібно додати біти (як вище), а іноді нам потрібно видалити біти (якщо дієслово містить тоді)

111 #decimal 7
-
100 #decimal 4
--------------
011 #decimal 3

з python: 7 & ~ 4 = 3 (видаліть із 7 біт, що представляють 4)

001 #decimal 1
-
100 #decimal 4
--------------
001 #decimal 1

з python: 1 & ~ 4 = 1 (вилучіть з 1 біт, який представляє 4 - у цьому випадку 1 не є "містить" 4).


0

Хоча маніпулювання бітами цілого числа є корисним, часто для мережевих протоколів, які можуть бути вказані внизу до біта, можна вимагати маніпуляції з більш довгими байтовими послідовностями (які не легко перетворюються в одне ціле число). У цьому випадку корисно використовувати бібліотеку бітових рядків, яка дозволяє здійснювати побітові операції над даними - наприклад, можна імпортувати рядок 'ABCDEFGHIJKLMNOPQ' у вигляді рядка або у вигляді шістнадцяткових та переміщувати його (або виконувати інші побітові операції):

>>> import bitstring
>>> bitstring.BitArray(bytes='ABCDEFGHIJKLMNOPQ') << 4
BitArray('0x142434445464748494a4b4c4d4e4f50510')
>>> bitstring.BitArray(hex='0x4142434445464748494a4b4c4d4e4f5051') << 4
BitArray('0x142434445464748494a4b4c4d4e4f50510')

0

наступні побітні оператори: & , | , ^ і ~ повернення значень (виходячи з їх введення) таким же чином, як логічні ворота впливають на сигнали. Ви можете використовувати їх для імітації схем.


0

Щоб перевернути біти (тобто доповнення / інвертування 1), ви можете зробити наступне:

Оскільки значення EXORed з усіма 1s призводить до інверсії, для заданої ширини бітів ви можете використовувати ExOR для їх інвертування.

In Binary
a=1010 --> this is 0xA or decimal 10
then 
c = 1111 ^ a = 0101 --> this is 0xF or decimal 15
-----------------
In Python
a=10
b=15
c = a ^ b --> 0101
print(bin(c)) # gives '0b101'
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.