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

Почему Java маскирует операнды с 0x1F?

В Java:

(0xFFFFFFFF <<  1) = 0xFFFFFFFE = 0b1111111111111110
                :         :               :
(0xFFFFFFFF << 30) = 0xE0000000 = 0b1110000000000000
(0xFFFFFFFF << 30) = 0xC0000000 = 0b1100000000000000
(0xFFFFFFFF << 31) = 0x80000000 = 0b1000000000000000

Однако:

(0xFFFFFFFF << 32) = 0xFFFFFFFF = 0b1111111111111111

Логически это не имеет никакого смысла, но, по моему мнению, это Java, выполняющая операцию, похожую на:

a << (b % Integer.SIZE) [edit, по-видимому:] a << (b & 0x1F)

Это относится и к >> и >>>.

Очевидно, что сдвиг по >= 32 (в случае Integer) удаляет все данные из типа данных, но есть моменты, когда это полезно. Например:

int value = 0x3F43F466; // any value
int shift = 17; // any value >= 0
int carry = value & (-1 << (Integer.SIZE - shift));
if (carry > 0)
    ; // code...

Конечно, это может быть исправлено, но найти эти ошибки можно довольно много (я просто потратил часы, отслеживая аналогичный вариант). Итак, мой вопрос: Есть ли причина для отказа от логического значения при смещении всех битов?

UPDATE:

Я попробовал это на C99, используя следующее:

#include<stdio.h>
main()
{
   int i, val;
   for (i = 0; i <=36; i++) {
       val = (-1 << i);
       printf("%d :\t%d\n", i, val);
   }
}

Я обнаружил, что он ведет себя так же, как Java, маскируя i & 0x1F, тогда как он дает предупреждение при компиляции при заданном постоянном значении:

warning: left shift count >= width of type
4b9b3361

Ответ 1

Конечно, есть: как большинство процессоров (в частности, x86) реализуют смещение битов и делают то, что вы хотите - проверить, больше ли сдвиг, чем 32, и если да, то вернуть нуль - требуется ветвь, что может быть дорогостоящим для современных процессоров. Это не просто "раздражает", это может замедлить работу на порядок.

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

Для справки логика не совсем такая же, как %, это маска. Подробнее см. JLS 15.19:

Если продвинутый тип левого операнда является int, в качестве расстояния сдвига используются только пять младших бит правого операнда. Это как если бы правый операнд подвергался поразрядному логическому оператору И и (§15.22.1) с значением маски 0x1f (0b11111). Поэтому фактически используемое расстояние сдвига всегда находится в диапазоне от 0 до 31 включительно.

Ответ 2

JLS 15.19 Если продвинутый тип левого операнда является int, в качестве расстояния сдвига используются только пять младших бит правого операнда. Это как если бы правый операнд подвергался поразрядному логическому оператору И и (§15.22.1) с значением маски 0x1f (0b11111). Поэтому фактически используемое расстояние сдвига всегда находится в диапазоне от 0 до 31 включительно

просто 0xFFFFFFFF << 32 эквивалентен to 0xFFFFFFFF << (32 & 0x1f) эквивалентен 0xFFFFFFFF << 0