Правый сдвиг для выполнения деления на 2 на -1

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

Для простоты возьмите систему с 4-разрядными номерами

-1 - 1111
-2 - 1110
-3 - 1101
-4 - 1100
-5 - 1011
-6 - 1010
-7 - 1001
-8 - 1000
7  - 0111
6  - 0110
5  - 0101
4  - 0100
3  - 0011
2  - 0010
1  - 0001
0  - 0000

Если я попытаюсь выполнить

6 / 2 = 0110 >> 1 = 0011 = 3
-6/ 2 = 1010 >> 1 = 1101 = -3

Действует как для + ve, так и для -ve number

Однако, когда вы достигаете 1

1 / 2 = 0001 >> 1 = 0000 = 0
-1/ 2 = 1111 >> 1 = 1111 = -1

Кажется, что есть специальный случай в -1, как правый сдвиг, чтобы переместить его в отрицательную бесконечность.

В настоящее время мне нужно поставить специальную, если проверить, так как я ожидаю -1/2 = 0.

Мне было интересно, как вы управляете этим исключением в своем коде? Вы, парень, поставили чек?

Ответ 1

Любое отрицательное нечетное число не будет работать. Однако, чтобы ответить на ваш вопрос, если вы знаете, что у вас могут быть отрицательные числа, просто разделите их на 2. Это превратилось в сдвиг с исправлением jit/компилятором.

Ответ 2

@Anon технически корректен.

Однако лучше всего использовать оператор / для деления и оставить микро-оптимизацию компилятору JIT. Компилятор JIT способен оптимизировать деления по константам в качестве последовательностей shift/add... когда это оптимальная задача для платформы выполнения.

Выполнение такого рода вещей (возможно) является преждевременной оптимизацией, и это может быть анти-оптимизация, если ваш код должен быстро запускаться на нескольких платформах Java.

Ответ 3

Если вы смещаете право на разделение на два, вы всегда оказываетесь "округляетесь" вниз - к нулю, если положительно, от него, если отрицательно.

Если это не то, что вы хотите, вы можете исправить его:

if (n & 1 > 0 && n < 0)
    result += 1;

Ответ 4

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

Почему вы думаете, что вам нужно делать деление с переключением битов, а не с более читаемым x / 2?

Ответ 5

Мне стало скучно в один прекрасный день, а профилированные делит против смены на 2 штуки; подумал, что я разместил его здесь для всех, кого это интересует.

В HotSpot VM 1.6 на Windows, используя j /= 4 от -100000000 до 100000000, заработало около 12 секунд, а j = (j >= 0) ? j >> 2 : ~(~j+1 >> 2) + 1; - всего лишь 2,5 секунды.

OpenJDK VM 1.6 на Linux получил 5.5s для делений и 1.5s для смен.

Это говорит о том, что компилятор JIT на самом деле ничего не делает для мощности 2-х делений.

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

~(~j+1 >> 2) + 1 использует два дополнения, чтобы перевернуть число положительное, сдвинуть и откинуть назад.

long j = 0;
for (long i = -100000000; i < 100000000; i++) {
    j = i;
    j /= 4;
}
System.out.println(j);`

vs

long j = 0;
for (long i = -100000000; i < 100000000; i++) {
    j = i;
    j = (j >= 0) ? j >> 2 : ~(~j+1 >> 2) + 1;
}
System.out.println(j);`

Ответ 6

В нечетном случае обе операции приводят к полудействию в результате.

  • 3/2 → floor (1.5) = > 1
  • -3/2 → floor (-1.5) = > -2

Вы можете поставить чек, например \

if ( isOdd(number) && isNegative(number) )
   result++;