Ответ 1
Любое отрицательное нечетное число не будет работать. Однако, чтобы ответить на ваш вопрос, если вы знаете, что у вас могут быть отрицательные числа, просто разделите их на 2. Это превратилось в сдвиг с исправлением jit/компилятором.
Я знаю, что я могу выполнить разделение на 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.
Мне было интересно, как вы управляете этим исключением в своем коде? Вы, парень, поставили чек?
Любое отрицательное нечетное число не будет работать. Однако, чтобы ответить на ваш вопрос, если вы знаете, что у вас могут быть отрицательные числа, просто разделите их на 2. Это превратилось в сдвиг с исправлением jit/компилятором.
@Anon технически корректен.
Однако лучше всего использовать оператор /
для деления и оставить микро-оптимизацию компилятору JIT. Компилятор JIT способен оптимизировать деления по константам в качестве последовательностей shift/add... когда это оптимальная задача для платформы выполнения.
Выполнение такого рода вещей (возможно) является преждевременной оптимизацией, и это может быть анти-оптимизация, если ваш код должен быстро запускаться на нескольких платформах Java.
Если вы смещаете право на разделение на два, вы всегда оказываетесь "округляетесь" вниз - к нулю, если положительно, от него, если отрицательно.
Если это не то, что вы хотите, вы можете исправить его:
if (n & 1 > 0 && n < 0)
result += 1;
Мне очень жаль это говорить, но я не обрабатываю это в своем коде, так как я не использую смещение битов для умножения или деления. Это пахнет преждевременной оптимизацией .
Почему вы думаете, что вам нужно делать деление с переключением битов, а не с более читаемым x / 2
?
Мне стало скучно в один прекрасный день, а профилированные делит против смены на 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);`
В нечетном случае обе операции приводят к полудействию в результате.
Вы можете поставить чек, например \
if ( isOdd(number) && isNegative(number) )
result++;