Я очень запутался в операции сдвига вправо на отрицательном числе, вот код.
int n = -15;
System.out.println(Integer.toBinaryString(n));
int mask = n >> 31;
System.out.println(Integer.toBinaryString(mask));
И результат:
11111111111111111111111111110001
11111111111111111111111111111111
Почему правое смещение отрицательного числа на 31 не 1 (знаковый бит)?
Поскольку в Java нет неподписанных типов данных, существуют два типа сдвигов вправо: арифметический сдвиг >>
и логический сдвиг >>>
. http://docs.oracle.com/javase/tutorial/java/nutsandbolts/op3.html
Арифметический сдвиг >>
будет хранить бит знака.
Неподписанный сдвиг >>>
не будет содержать знаковый бит (таким образом, заполняя 0
s).
(изображения из Википедии)
Кстати, и арифметический сдвиг влево, и логический сдвиг влево имеют одинаковый результат, поэтому есть только один сдвиг влево <<
.
10101010 << 1
? 11010100
или 01010100
?
0
с справа.
Оператор >>
называется Подписанный сдвиг вправо, сдвиньте все биты вправо определенное количество раз. Важно: >>
заполняет крайний левый бит (наиболее значимый бит 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
См. снова. Я пишу. Знак распространяется, поэтому левые три нули из-за знака бит.
Итак, это оператор >>
Signed right shift do, сохраняет знак левого операнда.
[ваш ответ]
В вашем коде вы меняете -15
вправо на 31
раз с помощью оператора >>
, поэтому ваши правые биты 31
освобождаются, а результаты - это все биты 1
, которые на самом деле -1
по величине.
Вы заметили, что таким образом -1 >> n
эквивалентно не утверждению.
Я считаю, что если сделать i = -1 >> n
, он должен быть оптимизирован для i = -1
компиляторами Java, но это другое дело
Далее, было бы интересно узнать, что в 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 (графическое программирование), хотя я его не использовал, но читал его где-то в прошлом.
Я бы предложил вам прочитать следующее: Разница между → > и " > : >>
- это арифметический сдвиг вправо, >>>
- логический сдвиг вправо.
Изменить:
Некоторый интересен оператор оператора без знака Shift Operator >>>
.
Беззнаковый оператор сдвига вправо >>>
создает чистое значение, которое является его левым операндом, смещенным вправо с нулевым расширением 0
на количество бит, указанное его правым операндом.
Как и >>
и <<
, оператор >>>
также оператор никогда не генерирует исключение.
Тип каждого операнда без знакового оператора сдвига вправо должен быть целым типом данных или возникает ошибка времени компиляции.
Оператор >>>
может выполнять преобразования типов в своих операндах; в отличие от арифметических двоичных операторов, каждый операнд преобразуется независимо. Если тип операнда является байтом, коротким или char, этот операнд преобразуется в int до вычисления значения оператора.
Тип значения, создаваемого беззнаковым оператором сдвига вправо, является типом его левого операнда. LEFT_OPERAND >>> RHIGT_OPERAND
Если преобразованный тип левого операнда является int, в качестве расстояния сдвига используются только пять наименее значимых бит значения правого операнда. (то есть 2 5= 32 бит = число бит в int)
Таким образом, расстояние сдвига находится в диапазоне от 0 до 31.
Здесь значение, созданное r >>> s
, совпадает с:
s==0 ? r : (r >> s) & ~(-1<<(32-s))
Если тип левого операнда длинный, то в качестве расстояния сдвига используются только шесть младших значащих бит значения правого операнда (то есть 2 5= 64 бит = число бит в длине)
Здесь значение, созданное r >>> s
, будет таким же, как и следующее:
s==0 ? r : (r >> s) & ~(-1<<(64-s))
Интересная ссылка: [Глава 4] 4.7 Операторы сдвига
Потому что → определяется как арифметический сдвиг вправо, который сохраняет знак. Чтобы получить эффект, который вы ожидаете, используйте логический сдвиг вправо, оператор → > .
preserves the sign
что это хорошо.
>>> -1
и это будет работать для типовint
иlong
.