В Java, когда используется бит-сдвиг, почему 1 << 32!= 1 << 31 << 1?

int a = 1 << 32; int b = 1 << 31 << 1;

Почему есть == 1? b равно 0, как я ожидал.

Ответы

Ответ 1

Все сдвиги выполняются по модулю 32 для int и mod 64 для longs.

Из раздел 15.19 спецификации:

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

Если продвинутый тип левого операнд long, то только шесть младшие разряды правого операнд используются в качестве сдвига расстояние. Это как если бы правая операнд подвергался поразному логического оператора И и (§15.22.1) с значение маски 0x3f. Смена дистанция фактически использующий поэтому всегда в диапазоне от 0 до 63, включительно.

Что касается того, почему язык был разработан таким образом - я не знаю, но С# имеет такое же дизайнерское решение. Здесь, в аннотированной спецификации ECMA С#, говорится:

С# сознательно держит поведение, определяемое реализацией, для miinimum. Они принимаются только тогда, когда влияние удара единообразное поведение будет чрезмерным (например, для некоторой точки с плавающей запятой точности). Следовательно, размер каждый интегральный тип и набор символов исправлено для Unicode.

Для операций сдвига тоже однородная поведение указано. Может быть достигнуто с использованием одного дополнительного инструкция (& 0x1F или или 0x3F), которая берет на себя лишь крошечные затраты на современные процессоров, тем более, что не справочная память. В отличие от операций с плавающей запятой, разница в поведении сдвига будет драматический, если оставить прихоть процессоры; а не разница в точности, полностью различные интегральные результаты будут производится.

При принятии этого решения комитет изучили справочные материалы для количество различных процессоров архитектуры. Есть немного согласованность в поведении сдвига рассчитывается вне диапазона -32.. + 32 для 32-битные операнды и, соответственно, -64.. + 64 для 64-разрядных операндов.

(Затем список некоторых примеров.)

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

Ответ 2

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

Например, процессоры IIRC, ARM (32-разрядные ISA) принимают наименее значащий байт регистра сдвига. (Сдвиги на самом деле не являются отдельными инструкциями по ARM).

Пока основной процессор имеет смутно разумный способ сдвига, легче очистить все, кроме наименее значимых битов (по одной инструкции обычно), чем проверять, большой ли сдвиг и разветвление (на самом деле на ARM это только типично добавляет одну инструкцию, потому что все инструкции являются условными).