Java: сдвиг вправо на отрицательное число

20

Я очень запутался в операции сдвига вправо на отрицательном числе, вот код.

int n = -15;
System.out.println(Integer.toBinaryString(n));
int mask = n >> 31;
System.out.println(Integer.toBinaryString(mask));

И результат:

11111111111111111111111111110001
11111111111111111111111111111111

Почему правое смещение отрицательного числа на 31 не 1 (знаковый бит)?

  • 2
    Кстати, вы можете использовать >>> -1 и это будет работать для типов int и long .
Теги:
bit-manipulation
bit-shift
bitwise-operators
negative-number

3 ответа

29
Лучший ответ

Поскольку в Java нет неподписанных типов данных, существуют два типа сдвигов вправо: арифметический сдвиг >> и логический сдвиг >>>. http://docs.oracle.com/javase/tutorial/java/nutsandbolts/op3.html

Арифметический сдвиг >> будет хранить бит знака.
Изображение 132104

Неподписанный сдвиг >>> не будет содержать знаковый бит (таким образом, заполняя 0 s).
Изображение 132105

(изображения из Википедии)


Кстати, и арифметический сдвиг влево, и логический сдвиг влево имеют одинаковый результат, поэтому есть только один сдвиг влево <<.

  • 0
    Что произойдет, если вы сделаете 10101010 << 1 ? 11010100 или 01010100 ?
  • 0
    @AurélienOoms AurélienOoms Это левый сдвиг, заполняющий 0 с справа.
Показать ещё 1 комментарий
12

Оператор >> называется Подписанный сдвиг вправо, сдвиньте все биты вправо определенное количество раз. Важно: >> заполняет крайний левый бит (наиболее значимый бит 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 Операторы сдвига

  • 0
    На самом деле он не «ставит 1», он просто сохраняет бит знака, каким бы он ни был.
  • 1
    Более простой ответ - и более правильный, и более полезный. Оператор не смотрит, является ли операнд отрицательным или положительным, а затем делает две разные вещи.
Показать ещё 6 комментариев
3

Потому что → определяется как арифметический сдвиг вправо, который сохраняет знак. Чтобы получить эффект, который вы ожидаете, используйте логический сдвиг вправо, оператор → > .

  • 0
    Хорошо, то, что я пытался сказать, что вы написали одним словом, preserves the sign что это хорошо.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню