JS. Неожиданное поведение при побитовом сдвиге влево (<<)

var a = 0b00000000000000000000000000000001 // 1
var b = a << 31 // -2147483648 (Логично, мы сдвинули биты на 31 влево)
var c = b << 1 // 0 (Тоже логично, единица вышла за пределы 32-х битного числа)
var d = a << 32 // 1

Ожидалось, что последняя строчка приравняет d = 0, ведь инструкция гласит: "a<<b Сдвигает двоичное представление a на b битов влево, добавляя справа нули".

> 1 << 32
1
> (1 << 31) << 1
0

Чем это вызвано и как этого избежать?


Ответы (2 шт):

Автор решения: Ростислав

Ответ на тот же вопрос в Java натолкнул меня на мысль, что если правая часть "<<" превышает 31, то от нее берется остаток от деления на 32, который и является итоговым сдвигом. Поэтому

(1<<32) == (1<<0); // Это истина

Избежать этого можно, если добавить проверку

if(b < 32) d = a << b; else d = 0;
→ Ссылка
Автор решения: Grundy

Согласно спецификации, при сдвиге влево (x << y), для чисел

  1. первый операнд приводится к Int32
  2. второй операнд приводится к UInt32
  3. от второго операнда берутся последние 5 бит (y & 0x1F)
  4. на полученный результат и происходит сдвиг

Возвращаясь к примеру в вопросе:

a << 32

3210=1000002

Последние 5 бит от 32 - 0

Таким образом происходит сдвиг на 0 бит - как результат: значение a как было 1, так и осталось.

→ Ссылка