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


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.