Оператор >>
, вызываемый Сдвиг вправо со знаком, сдвигает все биты вправо указанное количество раз. Важно то, что >>
заполняет самый левый бит знака (самый значащий бит MSB) до самого левого бита после сдвига. Это называется расширением знака и служит для сохранения знака отрицательных чисел, когда вы сдвигаете их вправо.
Ниже приведено мое схематическое представление с примером, показывающим, как это работает (для одного байта):
Пример:
i = -5 >> 3; shift bits right three time
Пять в форме дополнения до двух - это 1111 1011
Представление памяти:
MSB
+----+----+----+---+---+---+---+---+
| 1 | 1 | 1 | 1 | 1 | 0 | 1 | 1 |
+----+----+----+---+---+---+---+---+
7 6 5 4 3 2 1 0
^ This seventh, the left most bit is SIGN bit
И ниже, как работает >>
? Когда вы делаете -5 >> 3
this 3 bits are shifted
out and loss
MSB (___________)
+----+----+----+---+---+---+---+---+
| 1 | 1 | 1 | 1 | 1 | 0 | 1 | 1 |
+----+----+----+---+---+---+---+---+
| \ \
| ------------| ----------|
| | |
▼ ▼ ▼
+----+----+----+---+---+---+---+---+
| 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
+----+----+----+---+---+---+---+---+
(______________)
The sign is
propagated
Обратите внимание: самые левые три бита равны единице, потому что каждый бит знака сдвига сохраняется, и каждый бит тоже правый. Я написал Знак распространяется, потому что все эти три бита связаны со знаком (но не с данными).
Также из-за трех сдвигов вправо большинство трех битов теряются.
Биты между двумя правыми стрелками отображаются из предыдущих битов в -5
.
Я думаю, было бы хорошо, если бы я написал пример и для положительного числа. Следующий пример — 5 >> 3
, а пять — это один байт 0000 0101
.
this 3 bits are shifted
out and loss
MSB (___________)
+----+----+----+---+---+---+---+---+
| 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 |
+----+----+----+---+---+---+---+---+
| \ \
| ------------| ----------|
| | |
▼ ▼ ▼
+----+----+----+---+---+---+---+---+
| 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
+----+----+----+---+---+---+---+---+
(______________)
The sign is
propagated
Посмотрите еще раз, я пишу Знак распространяется, так что крайние три нуля связаны с битом знака.
Вот что делает оператор >>
Сдвиг вправо со знаком, сохраняя знак левого операнда.
[ваш ответ]
В вашем коде вы сдвигаете -15
вправо 31
раз, используя оператор >>
, так что ваши самые правые 31
биты теряются, и в результате все биты 1
фактически имеют величину -1
.
Вы заметили, что таким образом -1 >> n
эквивалентно не утверждению.
Я считаю, что если кто-то делает i = -1 >> n
, компиляторы Java должны оптимизировать его до i = -1
, но это другое дело
Далее, было бы интересно узнать, что в Java доступен еще один оператор сдвига вправо >>>
, который называется Сдвиг вправо без знака. И это работает логично и заполняет нуль слева для каждой операции сдвига. Таким образом, при каждом сдвиге вправо вы всегда получаете нулевой бит в крайнем левом положении, если вы используете оператор беззнакового сдвига вправо >>>
как для отрицательных, так и для положительных чисел.
Пример:
i = -5 >>> 3; Unsigned shift bits right three time
А ниже моя диаграмма, демонстрирующая, как работает выражение -5 >>> 3
?
this 3 bits are shifted
out and loss
MSB (___________)
+----+----+----+---+---+---+---+---+
| 1 | 1 | 1 | 1 | 1 | 0 | 1 | 1 |
+----+----+----+---+---+---+---+---+
| \ \
| ------------| ----------|
| | |
▼ ▼ ▼
+----+----+----+---+---+---+---+---+
| 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 |
+----+----+----+---+---+---+---+---+
(______________)
These zeros
are inserted
И вы можете заметить: на этот раз я не пишу, что знаковые биты распространяются, а на самом деле оператор >>>
вставляет нули. Следовательно, >>>
не сохраняет знак, а выполняет логический сдвиг вправо.
Насколько я знаю, сдвиг вправо без знака полезен в VDU (графическое программирование), хотя я не использовал его, но читал где-то в прошлом.
Я бы посоветовал вам прочитать это: Разница между >>> и >> : >>
— арифметический сдвиг вправо, >>>
— логический сдвиг вправо.
Изменить:
Немного интересного об операторе беззнакового сдвига вправо >>>
.
Оператор беззнакового сдвига вправо >>>
создает чистое значение, которое представляет собой его левый операнд, сдвинутый вправо с нулевым расширением 0
на число битов, указанное его правым операндом.
Подобно >>
и <<
, оператор >>>
тоже оператор никогда не выдает исключения.
Тип каждого операнда беззнакового оператора сдвига вправо должен быть целочисленным типом данных, иначе произойдет ошибка времени компиляции.
Оператор >>>
может выполнять преобразования типов своих операндов; в отличие от арифметических бинарных операторов, каждый операнд преобразуется независимо. Если тип операнда — byte, short или char, этот операнд преобразуется в int перед вычислением значения оператора.
Тип значения, создаваемого беззнаковым оператором сдвига вправо, является типом его левого операнда. LEFT_OPERAND >>> RHIGT_OPERAND
Если преобразованный тип левого операнда — int, в качестве расстояния сдвига используются только пять младших битов значения правого операнда. (то есть 25 = 32 бита = количество битов в int)
Таким образом, расстояние сдвига находится в диапазоне от 0 до 31.
Здесь значение, создаваемое r >>> s
, такое же, как:
s==0 ? r : (r >> s) & ~(-1<<(32-s))
Если тип левого операнда длинный, то в качестве расстояния сдвига используются только шесть младших битов значения правого операнда.(то есть 25 = 64 бита = количество бит в long)
Здесь значение, созданное r >>> s
, такое же, как следующее:
s==0 ? r : (r >> s) & ~(-1<<(64-s))
Интересная справка: [Глава 4] 4.7 Операторы сдвига
person
Grijesh Chauhan
schedule
17.03.2013
>>> -1
, и он будет работать для типовint
иlong
. - person Peter Lawrey   schedule 17.03.2013