Збільшення "замаскованих" бітсет


81

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

Я розглядаю замасковані бітсети, тобто бітсети, де встановлені біти є підмножиною маски, тобто 0000101з маскою 1010101. Я хочу досягти збільшення бітсету, але лише стосовно маскуваних бітів. У цьому прикладі результат буде 0010000. Щоб зробити це чіткіше, витягніть лише замасковані біти, тобто 0011збільште їх до 0100і розподіліть їх знову по бітах маски, даючи 0010000.

Хтось бачить ефективний спосіб зробити це, за винятком здійснення операції вручну, використовуючи комбінацію бітсканів та префіксних масок?

Відповіді:


123

Просто заповніть біти без маски такими, щоб вони розповсюджувались:

// increments x on bits belonging to mask
x = ((x | ~mask) + 1) & mask;

11
Це приємний фокус ... Майже магії, про яку я сказав, що її немає :)
Євген Ш.

8
@EugeneSh. Ніколи не вірте, що це не так.
zch

24
Імовірно, це не важливо для ОП, оскільки вони прийняли, але, мабуть, слід зазначити, що це обнулить біти, що не мають маски. Якби вони були потрібні в іншому місці, вам доведеться бути обережнішими при заміні x. Можливо x = (x & ~mask) | (((x | ~mask) + 1) & mask);.
TripeHound

2
@TripeHound Якби вони не були потрібні, який сенс був би навіть у використанні бітової маски?
someonewithpc

1
@someonewithpc Не впевнений, що ти хочеш сказати / запитати. Я не знаю, чому ОП потрібно збільшувати сусідній набір бітів, тому я не знаю, чи мають значення інші біти у вихідному значенні чи ні. Наприклад, якби початкове значення було 0101101(наприклад, .1.1.0.у бітах без маски та 0.0.1.1в "лічильнику"), чи потрібно їм буде0111000 (новий "лічильник" 0.1.0.0при збереженні .1.1.0.) або це просто 0010000прийнятно. Ця відповідь (і, можливо, інші, хоча я не перевіряв) дає останню; моя версія повинна дати першу, якщо це те, що потрібно.
TripeHound

20

Хоча це не інтуїтивно порівняно з прийнятою відповіддю, це працює лише за 3 кроки:

x = -(x ^ mask) & mask;

Це можна перевірити, як запропонував zch:

  -(x ^ mask)
= ~(x ^ mask) + 1  // assuming 2's complement
= (x ^ ~mask) + 1
= (x | ~mask) + 1  // since x and ~mask have disjoint set bits

Тоді це стає еквівалентом прийнятої відповіді.


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

Я думаю, що ваша перевірка була б набагато простішою, якби ви просто довели, що -(x ^ mask) == (x | ~mask) + 1всякий раз , коли x є підмножиною маски, а потім посилалися на мою відповідь.
zch

5
-(x^mask) == ~((x ^ mask) - 1) == ~(x ^ mask) + 1 == (x ^ ~mask) + 1 == (x | ~mask) + 1. Останнє рівняння виконується, оскільки бітові набори не перетинаються, інші завжди істинні (принаймні у 2-доповненні).
zch

1
Ті, хто цікавляться кроками, які я зробив для отримання цієї відповіді, можуть звернутися до цієї сторінки .
nglee

5
Можливо, варто зазначити, що вони не оптимізують однаково, що часто актуально для людей, які роблять біт- двідлінг : godbolt.org/g/7VWXas - хоча насправді коротший, здається, залежить від компілятора. Не уявляю, який з них буде швидшим, або якщо різниця значна.
Леушенко

7

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

Почнемо з

x = mask

і отримати попереднє значення за допомогою

x = (x - 1) & mask

x - 1частина змінює останній ненульовий біт на нуль і встановлює всі менш значущі біти на 1. Тоді & maskчастина залишає серед них лише шматочки маски.


2 операції, приємно. Однак я б стверджував, що це той самий підхід, лише розповсюджуючи запозичення через нулі, а не виконуючи їх.
zch

@zch, це правильно, дякую. Я переформулюю відповідь
DA,

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

@Jasen, звичайно. Але не складно встановити ці немаскові біти. І інші відповіді мають подібне питання.
DAle
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.