21

Я очень растерялся в работе правой смены на отрицательном числе, вот код.Java: сдвиг вправо на отрицательном числе

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'. –

ответ

30

Поскольку в Java нет беззнаковых типов данных, существует два типа правых сдвигов: arithmetic shift>> и logical shift>>>. http://docs.oracle.com/javase/tutorial/java/nutsandbolts/op3.html

Арифметический сдвиг >> будет хранить бит знака.
Arithmetic shift

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

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


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

+0

Что произойдет, если вы сделаете ** '10101010 << 1' **? ** '11010100' ** или **' 01010100' **? –

+0

@ 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 

См я снова пишет знак распространяется, поэтому три левых нулей должны подписать немного.

Таким образом, это оператор >>Подписана правая смена do, сохраняет знак левого операнда.

[ваш ответ]
В коде, вы сдвигаете -15 вправо для 31 раз, используя >> оператора, чтобы ваш самой правого 31 бит засыпки и результатов всех биты 1, что на самом деле -1 по величине.

Вы заметили, что таким образом -1 >> n эквивалентно утверждению.
Я верю, что если один сделать i = -1 >> n он должен быть оптимизирован для i = -1 с помощью Java-компиляторов, но это другое дело

Далее, было бы интересно узнать, в Java еще один оператор правого сдвига доступен >>> называется Unsigned Правый сдвиг. И он работает логически и заполняет ноль слева для каждой операции переключения. Поэтому при каждой правой смене вы всегда получаете бит нуля в левой позиции слева, если используете беззнаковый сдвиг вправо >>> для обоих отрицательных и положительных чисел.

Пример:

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 (графическое программирование), хотя я его не использовал, но читал его где-то в прошлом.

Я предлагаю вам прочитать следующее: Difference between >>> and >>: >> - это арифметический сдвиг вправо, >>> - это логический сдвиг вправо.

Edit:

Некоторые интересные про сдвиг вправо без знака оператора >>> оператора.

  • без знака оператор правого сдвига >>> производит чистое значение, которое является его левый операнд справа сдвигается с нулевым расширением 0 по количеству битов, указанных правого операнда.

  • и <<, оператор >>> также оператор не исключает.

  • Тип каждого операнда без знакового оператора сдвига вправо должен быть целым типом данных или возникает ошибка времени компиляции.

  • Оператор >>> может выполнять преобразования типов на своих операндах; в отличие от арифметических двоичных операторов, каждый операнд преобразуется независимо. Если тип операнда является байтом, коротким или символом, этот операнд преобразуется в int до вычисления значения оператора.

  • Тип значения, созданного беззнаковым оператором сдвига вправо, является типом его левого операнда.LEFT_OPERAND >>> RHIGT_OPERAND

  • Если преобразованный тип левого операнда INT, только пять младшие биты от значения правого операнда используются в качестве расстояния сдвига. (, что составляет 2 = 32 бит = количество бит в Int)
    Таким образом, расстояние сдвига находится в диапазоне от 0 до 31.

    Здесь значение получают r >>> s такое же, как:

    s==0 ? r : (r >> s) & ~(-1<<(32-s)) 
    
  • Если тип левого операнда имеет длину, то только шесть младших битов значения правого операнда используются в качестве расстояния сдвига. (, что составляет 2 = 64 бита = число бит в длинном)

    Здесь значение получают r >>> s такое же, как следующее:

    s==0 ? r : (r >> s) & ~(-1<<(64-s)) 
    

интересный Ссылка: [Chapter 4] 4.7 Shift Operators

+0

Это действительно не «положить 1», он просто сохраняет бит знака, что бы это ни было. – EJP

+1

Более простой ответ является более правильным и более полезным. Оператор не видит, является ли операнд отрицательным или положительным, а затем выполняет две разные вещи. – EJP

+0

Пожалуйста, придерживайтесь точки и не оставляйте личность. Ваше объяснение в два раза сложнее, чем необходимо, и, судя по вашему комментарию к ответу @ AlvinWong, вы все еще не понимаете, почему. – EJP

3

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

+0

Хорошо, что я пытался сказать, что вы написали одним словом * сохраняет знак *, что хорошо. –

Смежные вопросы