Операторы сдвига битов делают именно то, что подразумевает их имя. Они сдвигают биты. Вот краткое (или не очень краткое) введение в различные операторы сдвига.
Операторы
>>
является арифметическим (или подписанным) оператором правого сдвига.
>>>
является логическим (или беззнаковым) правым оператором сдвига.
<<
является оператором левого сдвига и отвечает потребностям как логических, так и арифметических сдвигов.
Все эти операторы могут быть применены к целочисленных значений ( 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
результаты в 3,221,225,472:
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.