sidoshi
sidoshi

Reputation: 2160

Why does right shift on positive number sometimes result in a negative number?

Right shifting a number in javascript sometimes results in a negative number. What is the reason behind that? Can that be mitigated?

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

Upvotes: 2

Views: 671

Answers (2)

Robby Cornelissen
Robby Cornelissen

Reputation: 97162

Use the zero-fill right shift operator (>>>) to always get a positive result:

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

The reason for the >> operator returning the number is caused by the fact that, originally, the number is internally represented as a 64-bit floating point number:

10110101110110111000000111010000100000110

The bit shift operation will first convert the operand to a 32-bit integer. It does this by keeping only the 32 least significant bits, and discarding the rest:

10110111000000111010000100000110

Then it will shift it by the specified number of bits while maintaining the sign, i.e. shifting in 8 1 bits from the left:

11111111101101110000001110100001

Converting back to decimal, this yields:

-4783199

Upvotes: 3

user555045
user555045

Reputation: 64904

The basic issue is that 1562143596806 is too large to fit in 32 bits. It can be represented as a Number, but when performing bitwise operations, the value is first converted to a 32bit integer and that means the "top bits" are already dropped before shifting - the upper bits of the result are therefore not filled from the original value, they are copies of the sign of that temporary 32bit value (or with >>>, they would be zero, which is not really an improvement). That the result happens to come out negative is just an accident depending on the exact bit pattern of the input, if it had been positive it would still have been the wrong positive value.

Such large values could be safely manipulated as BigInt, but support for that is lacking. Using floating point arithmetic can work, but requires extra care. For example you can divide by 256 and floor the result, but you cannot use the usual |0 to get rid of the fractional part, because even after dividing by 256 the value is too big to fit in 32 bits. Various non-built-in BigInt libraries exist to deal with this sort of thing too.

Upvotes: 1

Related Questions