Подтвердить что ты не робот

Какова цель беззнакового оператора сдвига ">>>" в Java?

Я понимаю, что делает беззнаковый оператор сдвига " → > " в Java, но зачем он нам нужен и почему нам не нужен соответствующий беззнаковый оператор сдвига влево?

4b9b3361

Ответ 1

Оператор >>> позволяет обрабатывать int и long как 32- и 64-разрядные неподписанные интегральные типы, отсутствующие на языке Java.

Это полезно, когда вы меняете то, что не представляет числовое значение. Например, вы можете представлять черно-белое изображение с битовой картой с использованием 32-разрядных int s, где каждый int кодирует 32 пикселя на экране. Если вам нужно прокрутить изображение вправо, вы предпочтете, чтобы биты слева от int стали нулями, чтобы вы могли легко поместить биты из соседнего int s:

 int shiftBy = 3;
 int[] imageRow = ...
 int shiftCarry = 0;
 // The last shiftBy bits are set to 1, the remaining ones are zero
 int mask = (1 << shiftBy)-1;
 for (int i = 0 ; i != imageRow.length ; i++) {
     // Cut out the shiftBits bits on the right
     int nextCarry = imageRow & mask;
     // Do the shift, and move in the carry into the freed upper bits
     imageRow[i] = (imageRow[i] >>> shiftBy) | (carry << (32-shiftBy));
     // Prepare the carry for the next iteration of the loop
     carry = nextCarry;
 }

В приведенном выше коде не обращается внимание на содержимое трех верхних битов, потому что оператор >>> делает их

Не существует соответствующего оператора <<, поскольку операции с левым сдвигом в типах подписанных и неподписанных типов идентичны.

Ответ 2

>>> - также безопасный и эффективный способ поиска округленного среднего из двух (больших) целых чисел:

int mid = (low + high) >>> 1;

Если целые числа high и low близки к наибольшему целому числу машин, то это будет правильным, но

int mid = (low + high) / 2;

может получить неправильный результат из-за переполнения.

Здесь используется , исправляя ошибку в наивном двоичном поиске.

Ответ 3

В основном это связано со знаком (числовые сдвиги) или беззнаковыми сдвигами (обычно связанные с пикселями).

Поскольку левый сдвиг, во всяком случае, не касается знакового бита, это то же самое (< < < <)...

В любом случае мне еще предстоит встретиться с кем-то, кому нужно было использовать → > , но я уверен, что они там делают потрясающие вещи.

Как вы только что видели, оператор → автоматически заполняет бит высокого порядка с его предыдущим содержимым каждый раз, когда происходит смена. Это сохраняет знак значения. Однако иногда это нежелательны. Например, если вы меняете то, что не представляют числовое значение, вам может не потребоваться расширение знака место. Эта ситуация распространена, когда вы работаете с пиксельными значений и графики. В этих случаях вы, как правило, хотите ноль в бит высокого порядка независимо от его начального значения. Это называется сдвигом без знака. Для этого вы будете использовать javas без знака, оператор shift-right, → > , который всегда сдвигает нули в бит высокого порядка.

Дальнейшее чтение:

http://henkelmann.eu/2011/02/01/java_the_unsigned_right_shift_operator

http://www.java-samples.com/showtutorial.php?tutorialid=60

Ответ 4

Подписанный оператор с правосторонним сдвигом полезен, если у него есть int, который представляет число, и один хочет разделить его на две силы, округляя их до отрицательной бесконечности. Это может быть приятно, когда вы делаете такие вещи, как масштабирование координат для отображения; он не только быстрее, чем деление, но и координаты, которые отличаются масштабным коэффициентом до масштабирования, будут отличаться на один пиксель позже. Если вместо использования shifting используется разделение, это не сработает. При масштабировании в два раза, например, -1 и +1 отличаются на два, и поэтому они должны отличаться на один, а -1/2 = 0 и 1/2 = 0. Если вместо этого используется подписанный сдвиг вправо, все будет хорошо выглядеть: -1 → 1 = -1 и 1 → 1 = 0, правильно отдавая значения на один пиксель друг от друга.

Беззнаковый оператор полезен либо в тех случаях, когда ожидается, что на входе будет установлен только один бит, и один захочет, чтобы результат тоже сделал это, или в тех случаях, когда один будет использовать цикл для вывода всех бит одним словом, и хочет, чтобы оно прекратилось чисто. Например:

void processBitsLsbFirst(int n, BitProcessor whatever)
{
  while(n != 0)
  {
    whatever.processBit(n & 1);
    n >>>= 1;
  }
}

Если код должен был использовать подписанную операцию смены вправо и был передан отрицательное значение, он будет выводить 1 на неопределенный срок. Однако с оператором unsigned-right-shift самый старший бит заканчивается интерпретацией, как и любой другой.

Беззнаковый оператор сдвига вправо может также быть полезен, когда вычисление будет арифметически давать положительное число от 0 до 4 294 967 295, и каждый хочет разделить это число на две силы. Например, при вычислении суммы двух значений int, которые, как известно, являются положительными, можно использовать (n1+n2)>>>1 без необходимости продвигать операнды до long. Кроме того, если вы хотите разделить положительное значение int на что-то вроде pi без использования математики с плавающей запятой, можно вычислить ((value*5468522205L) >>> 34) [(1L < 34)/pi - 5468522204,61, который округляет значения 5468522205]. Для дивидендов по сравнению с 1686629712 вычисление value*5468522205L приведет к "отрицательному" значению, но поскольку известно, что арифметически правильное значение является положительным, использование беззнакового сдвига вправо позволит использовать правильное положительное число.

Ответ 5

Нормальный правый сдвиг >> отрицательного числа будет отрицательным. То есть бит знака будет сохранен.

Справа shift >>> без знака также сдвинет знаковый бит, заменив его нулевым битом.

Нет необходимости иметь эквивалентный сдвиг влево, потому что есть только один бит знака, и это самый левый бит, поэтому он только мешает при правильном смещении.

По существу, разница заключается в том, что один сохраняет знаковый бит, а другой - в нулях для замены знакового бита.

Для положительных чисел они действуют тождественно.

Пример использования >> и >>> см. BigInteger shiftRight.

Ответ 6

В наиболее типичных приложениях домена Java способ избежать переполнения заключается в использовании литья или Big Integer, например int в предыдущих примерах.

int hiint = 2147483647;
System.out.println("mean hiint+hiint/2 = " + ( (((long)hiint+(long)hiint)))/2);
System.out.println("mean hiint*2/2 = " + ( (((long)hiint*(long)2)))/2);

BigInteger bhiint = BigInteger.valueOf(2147483647);
System.out.println("mean bhiint+bhiint/2 = " + (bhiint.add(bhiint).divide(BigInteger.valueOf(2))));