Як називається зберігання / упаковка багатьох булевих станів в одне число?


55

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

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

Наприклад, ось дуже проста реалізація, призначена для ілюстрації концепції:

packStatesIntoNumber () {
  let num = 0
  if (this.stateA) num += 1
  if (this.stateB) num += 2
  if (this.stateC) num += 4
  if (this.stateD) num += 8
  if (this.stateE) num += 16
  if (this.stateF) num += 32
  return num
}

unpackStatesFromNumber (num) {
  assert(num < 64)
  this.stateF = num >= 32; if (this.stateF) num -= 32
  this.stateE = num >= 16; if (this.stateE) num -= 16
  this.stateD = num >= 8; if (this.stateD) num -= 8
  this.stateC = num >= 4; if (this.stateC) num -= 4
  this.stateB = num >= 2; if (this.stateB) num -= 2
  this.stateA = num >= 1; if (this.stateA) num -= 1
}

Ви також можете використовувати побітові оператори, розбирати число базових 2, перерахунки ... Є багато більш ефективних способів його реалізації, мене цікавить назва підходу в цілому.


8
У C # є enums, і вони можуть мати Flagsатрибут. Вони можуть зробити ваш код набагато простішим.
— Бернхард Хіллер

12
Я б назвав це "імітацією бітових полів". Це майже завжди погана ідея, якщо ефективність простору не є надзвичайно важливою.
— Кіліан Фот

7
@KilianFoth A boolзазвичай зберігається як 32-бітове ціле число. Таким чином, упаковка може змінити коефіцієнт 32. Це дійсно багато. Я маю на увазі, ми, програмісти, завжди готові викинути половину наших ресурсів, але я взагалі неохоче викидаю 97% з них. Такі відхідні фактори можуть легко змінити значення між можливістю запуску важливих випадків використання та втратою пам'яті.
— cmaster

3
Історично зазвичай типові бітові маски використовуються для оголошення, встановлення та отримання значень. Використання зрушень є дивним і насправді не найкращою ілюстрацією підходу.
— JimmyJames

3
@cmaster Причина збереження зберігається таким чином, оскільки обмін одним місцем пам'яті (32 або 64 біти на сучасних машинах) може бути дуже поганим для кеш-продуктивності, якщо ви не приділяєте багато уваги кодовому коду мови. Якщо у вас справді величезна кількість бітів, це, мабуть, варто, але якщо ні, то вам, ймовірно, краще не попередньо оптимізувати, а просто упакувати біти, коли ви готові передати мережу чи диск.
— Білл К

Відповіді:


107

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

Багато мов програмування мають допоміжні структури, які допомагають у цьому. Як зазначає @BernhardHiller у коментарях, C # має перерахунки із прапорами ; У Java є клас EnumSet .


4
Я би інтерпретував "бітове поле" як використання мовної функції, яка дозволяє окремим бітам призначати поля структури, а не робити це вручну з побітними операторами.
— Пітер Грін

22
@PeterGreen Це було б інакше, ніж стандартне тлумачення.
— Ерік

1
"Бітові карти" або "Бітові карти", хоча вони звичайні для наборів записів і обробки масивів, також можуть застосовуватися в цьому випадку. При витягуванні загальних елементів з декількох наборів значення можна розкласти для ідентифікації компонентів об'єднаної моделі. Це ми навіть говоримо про восьмі цифри файлового модуля. Бітові маски (будь-які маски), як правило, є фільтрами (як для портів вводу-виводу і регістрів спрямованості даних).
— mckenzm

1
C # також має BitArray, що дозволяє зберігати довільну кількість бітів та індексувати їх (тоді як прапори обмежені цілим типом і призначені для використання в якості масок).
— Луань

Правда; Я щойно згадав про дві структури, які мені найбільше знайомі. Напевно, там десятки, особливо іншими мовами.
— Глорфіндель

20

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

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

http://www.kinematicsoup.com/news/2016/9/6/data-compression-bit-packing-101

Що не чудово для цілей SO, але це найкраще визначення / опис, який я можу знайти, включаючи цей стислий опис: "Упаковка бітів - це проста концепція. Використовуйте якомога менше бітів для зберігання фрагмента даних".


Чи можете ви надати деякі довідки? Цікавий термін.
— Грег Бургхардт

13
Упаковка бітів технічно правильна, але також відноситься до більш загальної речі, ніж просто булевих станів - зберігання даних взагалі в найменшій кількості бітів. Наприклад, інше його використання може означати стиснення charмасиву, поклавши два chars в один int.
— Ізката

@GregBurghardt Знаєте, це цікаво. Я не замислювався над цим, коли розміщував, оскільки цей термін був таким поширеним у 80-х / 90-х, коли я вивчив програмування на C і збірку - тепер, хоча пошук у Google знайдеться МНОГО, не існує остаточної сторінки Вікіпедії для нього . Перша відповідь у google має таке визначення: "Упаковка бітів - це проста концепція. Використовуйте якомога менше бітів для зберігання фрагмента даних". kinematicsoup.com/news/2016/9/6/…
— Білл К

тоді я дізнався і про упаковку бітів, хоча ви можете отримати набагато божевільніше, ніж просто перевстановити невикористані 0 у те, що номінально було б цілими значеннями. кілька років тому я зіткнувся з системою, яка зберігала один з його параметрів як 8-бітний поплавок. 5 біт IIRC для безпідписаної мантіси (усі значення були позитивними, не потрібно чітко зберігати знак), і ще 3 для базового показника 10. На той момент я вважав, що це старий апаратний хитрість без шляху вперед, але коли машинне навчання нещодавно почало займатися з int4 vs int8, я міг побачити, як деякі робочі навантаження знижуються з FP16.
— Ден Нелі

1
@DanNeely Такі речі також часто підтримуються графічними процесорами - торгівля між точністю, пам’яттю та обчисленнями тут досить важлива. Це було досить добре використано і для обчислень на основі GPU.
— Луань

14

Для опису цього існує багато різних термінів.

Найчастіше біти називають "бітовими прапорами" або "бітовими полями".
(Однак варто зазначити, що "бітові поля" іноді посилаються на певну особливість мов C і C ++, яка пов'язана, але не зовсім однакова.)

Саме ціле число по-різному називається або "бітовим масивом", "бітовим набором" або "бітовим вектором", залежно від звичок і обставин.

Так чи інакше, витяг бітів з набору бітів / вектора / масиву здійснюється шляхом зсуву та маскування.
(тобто з використанням трохи маски .)


Деякі приклади кожного терміна в активному використанні:

  • Стаття Вікіпедії з цього приводу має назву Бітовий масив , де зазначається, що вона "також відома як бітова карта, бітовий набір, бітовий рядок або бітовий вектор"
  • Використовує C ++ std::bitset
  • Java використовує BitSet
  • C # використовує BitArray
  • StackOverflow має мітки bitvector, bitarrayіbitset
  • На PyPi є bitarrayпроект і BitVectorпроект

Це не дуже доречне питання, але я хочу сказати: будь ласка, не використовуйте додавання та віднімання для встановлення та очищення бітів, оскільки ці методи схильні до помилок.
(тобто якщо ви робите num += 1двічі, результат еквівалентний num += 2.)

Переважно використовувати замість них відповідні операції, якщо їх вибрана мова:

packStatesIntoNumber ()
{
  let num = 0
  if (this.stateA) num |= 1
  if (this.stateB) num |= 2
  if (this.stateC) num |= 4
  if (this.stateD) num |= 8
  if (this.stateE) num |= 16
  if (this.stateF) num |= 32
  return num
}

unpackStatesFromNumber (num)
{
  this.stateF = ((num & 32) != 0);
  this.stateE = ((num & 16) != 0);
  this.stateD = ((num & 8) != 0);
  this.stateC = ((num & 4) != 0);
  this.stateB = ((num & 2) != 0);
  this.stateA = ((num & 1) != 0);
}

1
this.stateF = (num & 32) ? true : falseтощо. Не потрібно мутувати numпід час вилучення значень.
— Роджер Ліпскомб

3
@RogerLipscombe Добре, я не дуже читав, що робив код, просто реагував на використання +та -. Зараз я пішов на краще і використовував != 0замість потрійного, який, на мою думку, є більш стислим, але все ще є експліцитом.
— Фарап
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.