March 2, 2015

JavaScript: How to extract lower 32 bit and higher 32 bit from 64 bit (53 bit) integer




In JavaScript all numbers are represented using 53 bits. JavaScript uses floating point representation to store all numbers internally, which means that integers are stored as floating point numbers (mantissa has 53 bits). This blog post is a good read on this subject.

So with 53 bits we can represent max 2^53 = 9007199254740992.

Unlike other languages such as C, C#, you cannot use right shift and AND binary operations to extract lower 32 bits and higher 21 bits from 53 bit numbers in JavaScript.

The reason is when we apply binary operator on any number - JavaScript first convert that number to 32 bit signed number, apply the binary operation and return the result. This means any bit that sits in position higher than 32 will be discarded.

I have used following approach to extract the higher (21 bit) and lower (32 bits) portions from a positive number <= 2^53.

function Get32BitPartsBE (bigNumber) {
    if (bigNumber > 9007199254740991) {
         // Max int that JavaScript can represent is 2^53.
         throw new Error('The 64-bit value is too big to be represented in JS :' + bigNumber);
    }

     var bigNumberAsBinaryStr = bigNumber.toString(2);
     // Convert the above binary str to 64 bit (actually 52 bit will work) by padding zeros in the left
     var bigNumberAsBinaryStr2 = '';
     for (var i = 0; i < 64 - bigNumberAsBinaryStr.length; i++) {
         bigNumberAsBinaryStr2 += '0';
     };

      bigNumberAsBinaryStr2 += bigNumberAsBinaryStr;

      return {
           highInt32: parseInt(bigNumberAsBinaryStr2.substring(0, 32), 2);
           lowInt32: parseInt(bigNumberAsBinaryStr2.substring(32), 2);
     };
}

The usage is:

var bigNumber = Math.pow(2, 53) - 10;
var r = Get32BitPartsBE(bigNumber);
// Confirm above logic is correct by building the bigNumber from two parts
assert((r.highInt32 * Math.pow(2, 32) + r.lowInt32) === bigNumber);

Why I had to write above function?

I had a requirement to write a 64 bit number to JavaScript buffer instance (yes, this number is < 2^53 but greater than 2^32)

There is no API to write 64 bit number to buffer, buffer has a method to write 32 bit integer writeUInt32BE

So we can  use writeUInt32BE twice to write 64 bit number - but we need lower 32 bits and upper 32 bits so that writeUInt32BE can consume.

The above function helped here.

function writeUInt64BE(buffer, pos, val) {
    var r = Get32BitPartsBE(val);
     buffer.writeUInt32BE(r.highInt32, pos);
     buffer.writeUInt32BE(r.lowInt32, pos + 4);
}