Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JS bitwise shift operator '>>' not returning the correct result

I found this weird problem in JS. I have some native bindings to a board game api that uses bitboards for representing the game state. I am trying to manipulate these bitboards in JS to display the result in a web-based GUI (using electron).

The 1s in the bitboard represent the position of the pieces. Here is an example:

const bitboard = 0b100000010000000000000000000000000000;

However, when I do bitboard >>= 1;, the value magically becomes 0b1000000000000000000000000000.

Runnable example:

const bitboard = 0b100000010000000000000000000000000000; // 0b is binary literal
console.log(bitboard.toString(2));
console.log((bitboard >> 1).toString(2)); // .toString(2) prints the number in binary

Edit: The same code works in Rust which is the language that I am using on the native side.

like image 653
Luke Avatar asked Jan 25 '23 18:01

Luke


2 Answers

There's probably a duplicate floating around somewhere but the solution here is to use BigInt

BigInt is a built-in object that provides a way to represent whole numbers larger than 253 - 1, which is the largest number JavaScript can reliably represent with the Number primitive and represented by the Number.MAX_SAFE_INTEGER constant. BigInt can be used for arbitrarily large integers.

You just need to make sure that both operands for the right-shift operator are the same type.

const bitboard = BigInt("0b100000010000000000000000000000000000")
console.log(bitboard.toString(2))
console.log((bitboard >> 1n).toString(2)) // note "1n"
like image 113
Phil Avatar answered Jan 27 '23 08:01

Phil


There are limits on what sorts of numerical values JavaScript can bit-shift. Using Node I found the cut-off right here at 32 bits:

0b10000000000000000000000000000000 >>> 1 // 32 bits
# => 1073741824
0b100000000000000000000000000000000 >>> 1 // 33 bits
# => 0

Where it just breaks down. Your binary value is over that threshold so you can't shift it using JavaScript's default representation.

You can do this with BigInt:

BigInt('0b100000010000000000000000000000000000') >> BigInt(1)
# => 17314086912n

Where that is the correct value.

like image 41
tadman Avatar answered Jan 27 '23 07:01

tadman