Побітове та замість оператора модуля


91

Ми знаємо, що, наприклад, модуль потужності двох може бути виражений так:

  x % 2 inpower n == x & (2 inpower n - 1).

Приклади:

x % 2 == x & 1
x % 4 == x & 3
x % 8 == x & 7 

А як щодо загальної непридатності двох чисел?

Скажімо:

x% 7 ==?


8
@Neil - Modulo і Binary І є досить фундаментальними операціями, я думаю, вони приблизно однакові в будь-якій комп'ютерній мові.
Джеймс Колпак,

1
Я отримую трохи втомився від не бачачи мову відповідав :) Хоча я думаю , як правило , якщо вони не уточнюють, я вважаю , це означає , що C ++ або C. Цікаво , як це вірно ..
Гарет Claborn

1
Просто для тих, хто намагається це зрозуміти, загляньте на stackoverflow.com/a/13784820/1414639 . О, і в JS з V8 я отримую дуже незначний приріст продуктивності за допомогою побітових операторів.
Барді Харборо,

1
@JamesKolpack Побітову операцію можна виконати НАБАГАТО швидше на центральному процесорі, ніж за модулем. Насправді, загальним трюком складання нуля реєстру є XOR його з собою (через цей факт). Сьогодні компілятор, можливо, зможе оптимізувати модуль степеня двох, але я не знаю
Kaiser Keister

Відповіді:


70

Перш за все, це насправді неточно стверджувати

x % 2 == x & 1

Простий контрприклад: x = -1. У багатьох мовах, в тому числі Java, -1 % 2 == -1. Тобто %не обов'язково традиційне математичне визначення модуля. Java, наприклад, називає його "оператором залишку".

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

У підставі 10, наприклад, для неотрицательного N, N mod 10^kпросто приймаючи значущі kцифри.

Список літератури


1
-1 = -1 (mod 2), не впевнений, на що ви прийшли - ви маєте на увазі, що це не те саме , що залишок IEEE 754?
BlueRaja - Danny Pflughoeft

2
@BlueRaja: загальний залишок для -1 у моді 2 - це 1 en.wikipedia.org/wiki/Modular_arithmetic#Remainders
polygenelubricants

@BlueRaja: Якщо ви дозволяєте від’ємні числа, то, в чому ви в основному можете бути впевнені (тим більше, що жодна мова не була згадана), це те (a / b) / b + a % b == a, що для операторів типу C a та b цілі числа, b ненульові, а також те, що abs(a % b) < abs(b)з однаковими умовами.
Девід Торнлі,

1
@DavidThornley - припустимо, ви маєте на увазі (a / b)* b + a % b == a.
sfjac

40

Існує лише простий спосіб знайти модуль 2 ^ і чисел за допомогою побітового розряду.

Існує геніальний спосіб вирішення справ Мерсенна за посиланням, таких як n% 3, n% 7 ... Існують особливі випадки для n% 5, n% 255 та складні випадки, такі як n% 6.

Для випадків 2 ^ i, (2, 4, 8, 16 ...)

n % 2^i = n & (2^i - 1)

Складніші важко пояснити. Читайте, лише якщо вам дуже цікаво.


1
голосувати ++; Відмінне посилання, дякую за посилання. Раджу іншим поглянути, це варто прочитати, навіть якщо це дещо складно.
varzeak

посилання - найкраща частина відповіді.
Amit Kumar

n% 2 ^ i = n & (1 << i - 1)
Картік Сінгх

18

Це працює лише для степенів двох (і часто лише позитивних), оскільки вони мають унікальну властивість мати лише один біт, встановлений на '1' у своєму двійковому поданні. Оскільки жоден інший клас чисел не ділиться цією властивістю, ви не можете створювати розрядні та вирази для більшості модульних виразів.


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

Мені подобається, як ви формулюєте це: " це трохи змінює ситуацію "
j3141592653589793238

12

Це особливий випадок, оскільки комп’ютери представляють числа в базі 2. Це можна узагальнити:

(число) база % база х

є equivilent до останньої х чисел (номер) бази .


5

Є модулі, крім степенів 2, для яких існують ефективні алгоритми.

Наприклад, якщо x становить 32 біти без підпису int, тоді x% 3 = popcnt (x & 0x55555555) - popcnt (x & 0xaaaaaaaa)


4

Модуль "7" без оператора "%"

int a = x % 7;

int a = (x + x / 7) & 7;

3
Не працює для 10% 2 = 0. (10 + 10/2) & 2 = 15 & 2 = 2, Аналогічно 10% 6 = 4. (10 + 10/6) & 6 = 11 & 6 = 2
Sriram Murali

10
Крім того, чому ви хочете розділити, коли хочете уникати використання модуля? AFAIK, інструкція ділити така ж, як та, щоб отримати залишок.
Horse SMith

2
@SriramMurali Ось тому, що ви використовували парний мод, звичайно, це не спрацювало, це обхідний шлях для дивних, як сказав ОП.
ylun.ca

3

Не використовуючи побітове та ( &) оператор у двійковому, це не так. Ескіз доказу:

Припустимо, існувало значення k таке, що x & k == x % (k + 1), але k! = 2 ^ n - 1 . Тоді, якщо x == k , вираз, x & kздається, "працює правильно" і результат k . Тепер розглянемо x == ki : якщо в k були будь-які біти "0" , є деяке i більше 0, яке ki може виражатися лише 1-бітами в цих положеннях. (Наприклад, 1011 (11) повинен стати 0111 (7), коли з нього відняли 100 (4), у цьому випадку 000 біт стає 100, коли i = 4. ) Якщо біт із виразу k повинен змінитися з нуля одному представляти кі, тоді він не може правильно обчислити x% (k + 1) , який у цьому випадку повинен бути ki , але побітове логічне значення і отримання цього значення з урахуванням маски не існує.


2

У цьому конкретному випадку (мод 7) ми все ще можемо замінити% 7 бітовими операторами:

// Return X%7 for X >= 0.
int mod7(int x)
{
  while (x > 7) x = (x&7) + (x>>3);
  return (x == 7)?0:x;
}

Це працює, оскільки 8% 7 = 1. Очевидно, що цей код, мабуть, менш ефективний, ніж простий x% 7, і, звичайно, менш читабельний.


1

Використовуючи bitwise_and, bitwise_or і bitwise_not, ви не можете змінити будь-які бітові конфігурації на інші бітові конфігурації (тобто ці набори операторів "функціонально завершені"). Однак для таких операцій, як модуль, загальна формула була б досить складною, я б навіть не намагався її відтворити.

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