Почему сдвиг вправо на положительном числе иногда приводит к отрицательному числу? - PullRequest
2 голосов
/ 03 июля 2019

Сдвиг вправо числа в javascript иногда приводит к отрицательному числу.В чем причина этого?Можно ли это смягчить?

const now = 1562143596806 // UNIX timestamp in milliseconds
console.log(now >> 8) // -4783199

Ответы [ 2 ]

3 голосов
/ 03 июля 2019

Используйте оператор с нулевым заполнением вправо (>>>), чтобы всегда получать положительный результат:

const now = 1562143596806 // UNIX timestamp in milliseconds
console.log(now >>> 8)

Причина, по которой оператор >> возвращает число, вызвана тем фактом, что первоначально число внутренне представляется как 64-битное число с плавающей запятой :

10110101110110111000000111010000100000110

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

10110111000000111010000100000110

Затем он сместит его на указанное число битов , сохраняя при этом знак , то есть сдвиг в 8 1 бит слева:

11111111101101110000001110100001

Преобразование обратно в десятичное, это дает:

-4783199
1 голос
/ 03 июля 2019

Основная проблема в том, что 1562143596806 слишком велик, чтобы поместиться в 32 бита. Он может быть представлен как Number, но при выполнении побитовых операций значение сначала преобразуется в 32-разрядное целое число, и это означает, что «старшие биты» уже сброшены до сдвига - верхние биты следовательно, результат не заполняется из исходного значения, это копии знака этого временного 32-битного значения (или с >>>, они будут равны нулю, что на самом деле не является улучшением). То, что результат получился отрицательным, является просто случайностью, зависящей от точной битовой комбинации входных данных, если бы она была положительной, это все равно было бы неправильным положительным значением.

Можно безопасно манипулировать такими большими значениями, как BigInt, но поддержка для этого отсутствует. Использование арифметики с плавающей запятой может работать, но требует дополнительной осторожности. Например, вы можете разделить на 256 и получить результат в пол, но вы не можете использовать обычный |0, чтобы избавиться от дробной части, потому что даже после деления на 256 значение слишком велико, чтобы поместиться в 32 бита. Существуют различные не встроенные библиотеки BigInt для решения подобных задач.

...