Почему побитовый сдвиг с 0 в JavaScript дает странные результаты в некоторых случаях

Просто играл с необычными побитовыми операциями в JavaScript, и в некоторых случаях я получал некоторые странные результаты:

Обычные случаи

1 << 0            // returns 1, makes sense
100 << 0          // returns 100, makes sense
100 >> 0          // returns 100, definitely makes sense

Но при сдвиге на 0 бит все дают нуль

9E99 << 0         // returns 0 ..... Why all bits are cleared?
9E99 >> 0         // returns 0 also  ..... All bits cleared?
Infinity >> 0     // returns 0
Infinity << 0     // returns 0
-Infinity << 0    // returns 0 .... Can't explain why
-0 << 0           // also yields 0 not -0 itself
-0 >> 0           // also resolved to 0

Что делать, если бесконечность и побитовый сдвиг

1 << Infinity     // returns 1  .. no changes
1024 << Infinity  // returns 1024 .. no changes
1024 >> Infinity  // returns 1024 .. no changes either
Infinity >> Infinity      // 0
Infinity << Infinity      // 0

Те вышеперечисленные случаи не имеют для меня большого смысла. При смещении целого числа на нулевые биты значение не изменяется. Но когда вы сдвигаете Infinity на 0 бит, он фактически возвращает вас 0. Почему?

Я думал, что перенос любого числового значения на 0 бит не должен изменять его значение, не так ли?

Кроме того, когда сдвиг маленького целочисленного значения на бесконечные биты, значение не изменяется вообще. Но когда вы меняете Infinity на любые значения, вместо этого вместо этого заменяется на 0.

Мне действительно интересно, почему эти явления происходят? Существуют ли какие-либо спецификации или теория, объясняющие эти странные поведения?

Ответ 1

Из MDN:

Операнды всех побитовых операторов преобразуются в подписанные 32-разрядные целые числа в двух дополнительных форматах.

Все номера в JavaScript Номера с плавающей точкой двойной точности IEEE754. Они преобразуются в 32 битовые целые числа до применения побитовых операторов.

Точнее, этот процесс описан в ToInt32 в спецификации:

enter image description here

Как вы видите, точно описано преобразование Бесконечности и -0.

Это двоичное усечение, что также объясняет, почему 9E99 изменяется на 0: это наиболее очевидно, если вы посмотрите на 32 бита справа от (9E99).toString(2) (вставьте его в свою консоль, re all 0).

Ответ 2

Бит сдвиги и логические операции в JavaScript оцениваются как 32-битные целые числа. Присутствие < оператор требует преобразования любого объекта в int или 0, если преобразование не выполняется. Infinity или 99e9 действительны, то есть удваивают, но не могут преобразовать в int: 9e99 mod 2 ^ 32 == 0. Пример встречного примера 9e9 | 0 == 410065408 иллюстрирует использование модульной алгебры.

Это работает в обоих направлениях: inf < inf эквивалентно 0 < 0.

Ответ 3

Это соответствует ECMA-262.

В принципе, JS-компилятор преобразует аргументы с плавающей запятой в 32-битное целое, используя алгоритм, описанный в п. 9.5 ECMA-262. В большинстве случаев вы упомянули, что результат преобразования равен 0.