Оператори, що змінюють біт, роблять саме те, що випливає з назви. Вони зміщують шматочки. Ось короткий (або не дуже короткий) вступ до різних операторів зміни.
Оператори
>>
- це арифметичний (або підписаний) правий оператор зсуву.
>>>
- це логічний (або непідписаний) правий оператор зсуву.
<<
є лівим оператором зсуву і відповідає потребам як логічних, так і арифметичних зрушень.
Всі ці оператори можуть бути застосовані до цілочисельних значень ( int
, long
, можливо , short
і , byte
або char
). У деяких мовах застосування операторів зсуву до будь-якого типу даних, меншого, ніж int
автоматично, змінює розмір операнда, який буде рівним int
.
Зауважте, що <<<
це не оператор, оскільки це було б зайвим.
Також зауважте, що C і C ++ не розрізняють операторів правого зсуву . Вони забезпечують лише >>
оператора, а поведінка, що зміщує право, - це реалізація, визначена для підписаних типів. Решту відповіді використовують оператори C # / Java.
(У всіх основних реалізаціях C і C ++, включаючи GCC і Clang / LLVM, >>
на підписаних типах є арифметикою. Деякі коди передбачають це, але це не щось, що стандартні гарантії. Хоча це не визначено , однак, стандарт вимагає реалізації, щоб визначити його одним так чи інакше. Однак, зсув лівих від’ємних підписаних чисел - це невизначена поведінка (переливання підписаного цілого числа). Тому, якщо вам не потрібен арифметичний правий зсув, зазвичай, це гарна ідея зробити біт-перемикання з непідписаними типами.)
Зсув вліво (<<)
Цілі числа зберігаються в пам'яті у вигляді серії біт. Наприклад, число 6, збережене як 32-бітове, int
було б:
00000000 00000000 00000000 00000110
Переміщення цього бітового шаблону в одну позицію ліворуч ( 6 << 1
) призведе до числа 12:
00000000 00000000 00000000 00001100
Як бачите, цифри змістилися вліво на одну позицію, а остання цифра праворуч заповнена нулем. Ви також можете відзначити, що зміщення вліво еквівалентно множенню на сили на 2. Отже, 6 << 1
це еквівалентно 6 * 2
і 6 << 3
еквівалентно 6 * 8
. Хороший оптимізуючий компілятор замінить множення зі зрушеннями, коли це можливо.
Некругове зсув
Зверніть увагу, що це не кругові зрушення. Зсунення цього значення вліво на одну позицію ( 3,758,096,384 << 1
):
11100000 00000000 00000000 00000000
результати в 2121252572:
11000000 00000000 00000000 00000000
Цифра, яка зміщується "з кінця", втрачається. Він не загортається.
Логічний правильний зсув (>>>)
Логічний правий зсув - це зворотний зсув вліво. Замість того, щоб переміщувати біти вліво, вони просто рухаються вправо. Наприклад, зміщення числа 12:
00000000 00000000 00000000 00001100
праворуч на одну позицію ( 12 >>> 1
) повернеться наші початкові 6:
00000000 00000000 00000000 00000110
Отже, ми бачимо, що переміщення вправо еквівалентне поділу за повноваженнями 2.
Втрачені шматочки пішли
Однак зміна не може відновити "втрачені" біти. Наприклад, якщо ми змістимо цю схему:
00111000 00000000 00000000 00000110
ліворуч 4 позиції ( 939,524,102 << 4
), отримуємо 2,147,483,744:
10000000 00000000 00000000 01100000
а потім переміщуючи назад ( (939,524,102 << 4) >>> 4
), ми отримуємо 134,217,734:
00001000 00000000 00000000 00000110
Коли ми втратили біти, ми не можемо повернути первісну вартість.
Арифметичний правий зсув (>>)
Арифметичний правий зсув точно такий же, як логічний правильний зсув, за винятком того, що замість прокладки з нулем він прокладає найзначніший біт. Це тому, що найбільш значущим бітом є бітовий знак , або біт, який розрізняє додатні та від’ємні числа. За допомогою прокладки найзначнішим бітом арифметичний правий зсув зберігає знаки.
Наприклад, якщо ми інтерпретуємо цей бітовий зразок як від’ємне число:
10000000 00000000 00000000 01100000
маємо число -2,147,483,552. Зсув це праворуч на 4 позиції за допомогою арифметичного зсуву (-2,147,483,552 >> 4) дасть нам:
11111000 00000000 00000000 00000110
або число -134,217,722.
Отже, ми бачимо, що ми зберегли знак своїх від’ємних чисел, використовуючи арифметичний правий зсув, а не логічний правий зсув. І ще раз ми бачимо, що ми виконуємо поділ за потужностями 2.