Для чого корисні бітові оператори? [зачинено]


19

Мови програмування часто поставляються з різними бітовими операторами (наприклад, побітове зсув вліво-вправо, побітові AND, OR, XOR ...) Вони не дуже звикають, або, принаймні, такий мій досвід. Їх іноді використовують у програмуванні викликів, питаннях інтерв'ю, або їх потребують рішення, наприклад:

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

Напевно, вони, мабуть, мають мало реального використання. Я думаю, що вони повинні бути швидшими, оскільки вони безпосередньо маніпулюють пам'яттю на низькому рівні.

Чому такі зустрічаються в більшості мов програмування? Будь-які випадки використання в реальному світі?


@Anto - простий приклад - це надіслати клієнту дані вартістю 256 Кб одночасно зі швидкістю 256 слів (4096 байт).
Рамхаунд

1
"Не використовуючи жодного оператора рівності, створіть функцію, яка повертає істину, коли два значення рівні" - в C return !(x-y);:? Я не знаю
Ендрю Арнольд

@Andrew: Це рішення, але ви можете це зробити і з побітними операторами.
Анто

19
"Вони не дуже звикають" - Впевнені в цьому? Я їх постійно використовую. Ми не всі працюємо у вашій проблемній області.
Ед С.

2
Недостатньо для повного відповіді, але спробуйте прочитати чотири найпопулярніших біта байта без біту, а потім врахуйте, що деякі формати даних дуже щільно упаковані.
Брендан Лонг

Відповіді:


53

Ні, у них багато реальних програм і є основними операціями на комп'ютерах.

Вони використовуються для

  • Жонглювання блоками байтів навколо, які не входять у типи даних мов програмування
  • Перемикання кодування вперед і назад від великого до маленького ендіана.
  • Упаковка 4 6-бітних фрагментів даних у 3 байти для деякого послідовного або usb-з'єднання
  • Багато форматів зображень мають різну кількість бітів, призначених кожному кольоровому каналу.
  • Все, що включає штифти IO у вбудованих додатках
  • Стиснення даних, у яких часто немає даних, добре відповідає 8-бітовим межам. \
  • Алгоритми хешування, CRC або інші перевірки цілісності даних.
  • Шифрування
  • Генерація псевдовипадкових чисел
  • Raid 5 використовує побітовий XOR між томами для обчислення парності.
  • Тонн більше

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


1
+1 для вашого досить вичерпного списку, до якого ви навіть, здається, додаєте
Анто

28
+1. @Anto: Цей список ніде не є вичерпним. Вичерпний перелік випадків використання для побітових операторів в системному програмуванні буде таким самим, як і вичерпний список запитів SQL в бізнес-додатках. Факт забави: я весь час використовую побитові операції, але не писав заяву SQL протягом років ;-)
nikie

4
@nikie: І я постійно пишу SQL, але не використовую побітові оператори протягом років! :)
FrustratedWithFormsDesigner

3
Я працюю у вбудованих системах - бітові оператори - це хліб та масло. Використовується щодня без жодної думки.
quick_now

9
Якщо я періодично використовую бітшифтинг в SQL, чи отримую я приз?
Мураха

13

Тому що вони фундаментальні операції.

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

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

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

Наприклад:

// Given a 30-bit RGB color value as a 32-bit int
// A lot of image sensors spit out 10- or 12-bit data
// and some LVDS panels have a 10- or 12-bit format
b = (color & 0x000003ff);
g = (color & 0x000ffc00) >> 10;
r = (color & 0x3ff00000) >> 20;

// Going the other way:
color = ((r << 20) & 0x3ff00000) | ((g << 10) & 0x000ffc00) | (b & 0x000003ff);

Розшифровка інструкцій процесора для процесорів типу RISC (наприклад, при емуляції іншої платформи) вимагає витягування частин великого значення, як зазначено вище. Іноді виконання цих операцій з множенням та діленням та модулем тощо може бути вдесятеро повільніше, ніж еквівалентний бітовий оп.


12

Типовим прикладом є вилучення окремих кольорів із значення 24-бітового RGB і назад.


РЕДАКТУВАТИ: Від http://www.docjar.com/html/api/java/awt/Color.java.html

    value =  ((int)(frgbvalue[2]*255 + 0.5))    |
                (((int)(frgbvalue[1]*255 + 0.5)) << 8 )  |
                (((int)(frgbvalue[0]*255 + 0.5)) << 16 ) |
                (((int)(falpha*255 + 0.5)) << 24 );

Покажіть цей приклад на практиці? Фрагмент коду?
Анто

Кращим прикладом може бути обробка 16-бітових (4,5bpc) або 30-бітових (10bpc) значень RGB.
greyfade

@grey, сміливо додайте такі приклади.

6

Ось приклад реального світу, який ви знайдете в Quake 3, Quake 4. Doom III. Всі ті ігри, які використовували двигун Q3 .

float Q_rsqrt( float number )
{
        long i;
        float x2, y;
        const float threehalfs = 1.5F;

        x2 = number * 0.5F;
        y  = number;
        i  = * ( long * ) &y;                       // evil floating point bit level hacking [sic]
        i  = 0x5f3759df - ( i >> 1 );               // what the fuck? [sic]
        y  = * ( float * ) &i;
        y  = y * ( threehalfs - ( x2 * y * y ) );   // 1st iteration
//    y  = y * ( threehalfs - ( x2 * y * y ) );   // 2nd iteration, this can be removed

        return y;
}

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

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


+1 Для цих коментарів, навіть якщо вони не є вашими. Змусив мене посміятися.
Басинатор

4

Зсув швидше, ніж множення чи ділення на потужність дві. Наприклад, << = 2 множить a на 4. І навпаки, a >> = 2 ділить a на чотири. Також можна видалити дані на пристрій за допомогою операторів, що займаються бітом. Наприклад, ми можемо відправити N послідовних потоків даних з N-контактного порту, використовуючи операції shift, xor та "і" всередині N циклів. Все, що може бути досягнуто в цифровій логіці, також можна виконати на програмному забезпеченні та навпаки.


1
Будьте обережні, поділяючи з округленням вгору або вниз і т. Д. Зсув не враховує цього, тому я вважаю, що фактично краща практика використовувати розділення в коді, коли я маю на увазі поділ, і дозволити компілятору оптимізувати його в зсув і додати для мене.
Daemin

@Daemin: Я працюю з цілими числами, коли використовую цю техніку. Типовою поведінкою для цілого поділу на C і C ++ є усічення до нуля; отже, зміщення цілого права на силу двох дає такий самий результат, що і ділення цілого числа на два.
біт-твідлер

1
@ bit-twiddler Правий зсув не працює так само, як ділення на від'ємні числа.
Daemin

@Daemin: Ви, здається, пекла переконалися, що довели мене неправильно. По-перше, ви кидаєте проблему округлення. Коли я відкидаю цю заяву, заявляючи, що цей поділ у C і C ++ скорочується до нуля, ви викидаєте підписане ціле число. Де я сказав, що я застосовував оператора зсуву до підписаних додаткових цілих чисел? Зважаючи на це, можна все ж використовувати оператор зсуву для поділу на потужність дві. Однак, оскільки C і C ++ виконують і арифметичний правий зсув замість простого старого правого зсуву, спочатку потрібно перевірити, чи є значення негативним. Якщо значення негативне,
біт-твідлер

1
Точно будьте обережні, використовуючи зрушення як заміну множення та ділення, оскільки є тонкі відмінності. Не більше не менше.
Daemin

3

Давно давні бітові оператори були корисні. Сьогодні вони менше. О, вони не зовсім марні, але минуло давно, як я бачив одного, що використовувався, який мав би бути використаний.

У 1977 році я був програмістом з мовної збірки. Я переконався, що асемблер - це єдина справжня мова. Я був певен, що мова, як Паскаль, призначена для академічних вихідних, яким ніколи не довелося нічого реально зробити.

Потім я прочитав "Мова програмування на С" Керніган та Річі. Це повністю змінило мою думку. Причина? У нього були бітні оператори! Це була мова монтажу! Просто він мав інший синтаксис.

Ще в ті часи я не міг уявити написання коду без ands, ors, shift та rotate. У наш час я їх майже ніколи не використовую.

Отже, коротка відповідь на ваше запитання: "Нічого". Але це не зовсім справедливо. Тож довша відповідь: "Здебільшого нічого".


xkcd.com/378 приходить на думку.
Maxpm

Бітові оператори корисні і сьогодні. Той факт, що у вашому домені вони не використовуються, не робить його невикористаним або навіть не використовується дуже часто. Ось простий приклад: спробуйте реалізувати AES без бітових операторів. Це єдиний приклад того, що робиться на більшості комп'ютерів щодня сотні чи тисячі разів на день.
ПРОСТО МОЕ правильне ДУМКУ

Кодування / декодування даних без використання бітових операторів в кращому випадку болісно. Наприклад, додавання вкладень MIME до повідомлення вимагає, щоб ми могли обробляти кодування даних від трьох до чотирьох (так само кодування radix64).
біт-твідлер

2

Шифрування

Я пропоную переглянути дуже маленький фрагмент з алгоритму шифрування DES :

temp = ((left >>> 1) ^ right) & 0x55555555; right ^= temp; left ^= (temp << 1);
temp = ((right >>> 8) ^ left) & 0x00ff00ff; left ^= temp; right ^= (temp << 8);
temp = ((right >>> 2) ^ left) & 0x33333333; left ^= temp; right ^= (temp << 2);
temp = ((left >>> 16) ^ right) & 0x0000ffff; right ^= temp; left ^= (temp << 16);
temp = ((left >>> 4) ^ right) & 0x0f0f0f0f; right ^= temp; left ^= (temp << 4);

Хоча не впевнений, DES рекомендується сьогодні: P
Арман

@Alison: Ні, але алгоритми шифрування, які його замінили, передбачають ще більше операцій маніпулювання бітами. :-)
Carson63000

@Alison - звичайно, але TripleDES просто DES робиться 3 рази з 3-кратними бітними ключами.
Скотт Вітлок

2

Дуже багато хороших відповідей, тому я не повторюю ці вживання.

Я їх досить багато використовую в керованому коді (C # / .Net), і це не пов'язане з алгоритмами економії місця, високою продуктивністю або розумними бітовими алгоритмами. Іноді деяка логіка просто добре підходить для зберігання даних таким чином. Я часто використовую їх, коли у мене є перерахунки, але екземпляри можуть одночасно приймати кілька значень із цього перерахунку. Я не можу розмістити приклад коду з роботи, але швидкий google для "Flags enum" ("Прапори" - це C # спосіб визначення перерахунку, який використовується бітовим способом) дає цей приємний приклад: http: // www.dotnetperls.com/enum-flags .


2

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


1

Чому їх знайшли?

Ну, це, мабуть, тому, що вони відповідають інструкціям по збірці, а іноді вони просто корисні для речей на мовах вищого рівня. Те ж саме стосується страху, GOTOякий відповідає JMPінструкції по збірці.

Які їх використання?

Дійсно, для назви є просто багато застосувань, тому я просто надам недавнє, хоча і сильно локалізоване, використання. Я багато працюю з складанням 6502, і я працював над невеликим додатком, який перетворює адреси пам'яті, значення, порівнює значення тощо в коди, які можна використовувати для пристрою GameGenie (в основному, це чіт-додаток для NES). Коди створюються деякою бітною маніпуляцією.


1

У наші дні багато програмістів звикли до комп'ютерів з майже нескінченною пам'яттю.

Але деякі з цих програм все ще програмують крихітні мікроконтролери, де кожен біт підраховується (наприклад, у вас є лише 1 К або менше оперативної пам’яті), а побітні оператори дозволяють програмісту використовувати ці біти одночасно, а не витрачати на програму набагато більше об'єкта абстракції, ніж це може знадобитися для збереження певного стану, необхідного алгоритму. IO на цих пристроях може також вимагати зчитування або управління на бітовій основі.

"Реальний світ" має набагато більше тих крихітних мікроконтролерів, ніж сервери або ПК.

Що стосується чисто теоретичних типів CS, машини Тьюрінга - це загальні стани.


1

Ще одне з багатьох можливих застосувань побітових операторів ...

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

int  myFunc (bool, bool, bool, bool, bool, bool, bool, bool);

...

myFunc (false, true, false, false, false, true, true, false);

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

/* More descriptive names than MY_FLAGx would be better */
#define MY_FLAG1    0x0001
#define MY_FLAG2    0x0002
#define MY_FLAG3    0x0004
#define MY_FLAG4    0x0008
#define MY_FLAG5    0x0010
#define MY_FLAG6    0x0020
#define MY_FLAG7    0x0040
#define MY_FLAG8    0x0080

int  myFunc (unsigned myFlags);

...

myFunc (MY_FLAG2 | MY_FLAG6 | MY_FLAG7);

З більш описовими назвами прапорів він стає значно читабельнішим.


1

Якщо ви знаєте що-небудь про Unicode , ви, мабуть, знайомі з UTF-8. Він використовує купу тестів, змін і масок для упаковки 20-ти бітової кодової точки в 1 - 4 байти.


0

Я не використовую їх часто, але іноді вони стають у нагоді. На розум поводиться з керуванням Enum .

Приклад:

enum DrawBorder{None = 0, Left = 1, Top = 2, Right = 4, Bottom = 8}

DrawBorder drawBorder = DrawBorder.Left | DrawBorder.Right;//Draw right & left border
if(drawBorder & DrawBorder.Left == DrawBorder.Left)
  //Draw the left border
if(drawBorder & DrawBorder.Top == DrawBorder.Top)
  //Draw the top border
//...

0

Не впевнений, чи не було зазначено це використання ще:

Я бачу АБО досить багато, коли працюю з вихідним кодом підсвічування (openSolaris) для зменшення кількох повернених значень до 0 або 1, наприклад

int ret = 0;
ret |= some_function(arg, &arg2); // returns 0 or 1
ret |= some_other_function(arg, &arg2); // returns 0 or 1
return ret;
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.