Кілька днів тому член StackExchange Anto поцікавився, чи дійсно використовує для операторів біт- розум . Я заявив, що переміщення було швидше, ніж множення та ділення цілих чисел на два рівні. Член StackExchange Daemin протидіяв, заявивши, що зсув правої форми представляє проблеми з негативними числами.
На той момент я ніколи не задумувався над використанням операторів зсуву з підписаними цілими числами. Я в основному використовував цю техніку в розробці програмного забезпечення низького рівня; тому я завжди використовував непідписані цілі числа. C виконує логічні зрушення на непідписані цілі числа. При виконанні логічного зсуву справа не звертає уваги на бітовий знак. Відпущені біти заповнюються нулями. Однак C виконує операцію з арифметичним зсувом при зміщенні підписаного цілого числа справа. Випущені біти заповнюються бітом знака. Ця різниця призводить до того, що негативне значення округлюється до нескінченності, а не до усікання до нуля, що є іншою поведінкою, ніж підписане ціле ділення.
Кілька хвилин думки призвели до рішення першого порядку. Рішення умовно перетворює негативні значення в позитивні значення перед зміщенням. Значення умовно перетворюється назад в його негативну форму після виконання операції зсуву.
int a = -5;
int n = 1;
int negative = q < 0;
a = negative ? -a : a;
a >>= n;
a = negative ? -a : a;
Проблема цього рішення полягає в тому, що твердження умовного призначення зазвичай переводяться щонайменше на одну інструкцію стрибків, а інструкції стрибків можуть бути дорогими для процесорів, які не декодують обидва контури інструкцій. Повторне завантаження інструментального конвеєра вдвічі дає хорошу вм’ятину в будь-якому прирості продуктивності, отриманому при переході на ділення.
Зі сказаним вище я прокинувся в суботу з відповіддю на проблему умовного призначення. Проблема округлення, яку ми відчуваємо при виконанні операції арифметичного зсуву, виникає лише при роботі з представленням комплементу двох. Це не відбувається при представленні доповнення. Рішення проблеми передбачає перетворення значення доповнення двох у значення доповнення, перш ніж виконувати операцію зсуву. Тоді нам доведеться перетворити значення додатка назад у значення доповнення двох. Дивно, але ми можемо виконувати цей набір операцій без умовного перетворення негативних значень перед виконанням операції зсуву.
int a = -5;
int n = 1;
register int sign = (a >> INT_SIZE_MINUS_1) & 1
a = (a - sign) >> n + sign;
Від'ємне значення доповнення двох перетворюється на негативне значення доповнення, яке вичиняється. Зі зворотного боку негативне значення доповнення одного перетворюється на негативне значення доповнення двох, додаючи одне. Код, перерахований вище, працює тому, що біт знаків використовується для перетворення з доповнення двох у доповнення і навпаки . Лише негативні значення встановлюватимуть біти знаків; отже, знак змінної буде дорівнює нулю, коли a додатний.
З урахуванням сказаного, чи можете ви придумати інші хитрі хакерства, як той, що був вище, які зробили це у вашій сумці хитрощів? Який ваш улюблений біт-мудрий хак? Я завжди шукаю нові біт-розумні хаки, орієнтовані на ефективність.