BigInt: when the size matter

BigInt, Because the size does matter

There is nothing more joyful than to have one exception that breaks the entire app. I had this case with one of the most ignored downsides of JavaScript: large numbers. All the values we had in the app were small, but the user ID was a number bigger than JavaScript Number.MAX_SAFE_INTEGER (9007199254740991, Math.pow(2, 53).

As shown at the post about JavaScript mathematical edge cases “As stable as math can be“, once we have numbers bigger than the maximum safe number, as inside a black hole, the math laws just break:

Number.MAX_SAFE_INTEGER + 3 != Number.MAX_SAFE_INTEGER + 1 + 1 + 1;

To solve this problem Chrome v-67 introduced a new primitive type BigInt:

BigInt(Number.MAX_SAFE_INTEGER) + 3n == BigInt(Number.MAX_SAFE_INTEGER) + 1n + 1n + 1n;

As seen in the code above, there are two ways to define BigInt: with BigInt constructor, on with the ‘n’ prefix.

And since it’s a big integer, BigInt is integer 🙂

bigint(1.5);
// Uncaught RangeError: The number 1.5 cannot be converted to a BigInt because it is not an integer

11n / 3n == 3n; // Round down

BigInt is not a number, but a new primitive type, so you can not use numbers with BigInt on the same operation:

1 + 1n; 
// Uncaught TypeError: Cannot mix BigInt and other types, use explicit conversions

And since BigInt is a different type than numbers, most of the numeric function will fail on BigInt:

Math.max(1n);
// Uncaught TypeError: Cannot convert a BigInt value to a number

JSON.stringify({number:1n});
// Uncaught TypeError: Do not know how to serialize a BigInt

But you can compare, as long as you remember the difference between value comparison, and full comparison:

typeof(1) == 'number';
typeof(1n) == 'bigint';

1  == 1n; 
1 !== 1n;

1n == true;
0n == false;

Before changing all the math operations in the app from Numbers to BigInt, we need to know that as expected, BigInt is slower than numbers:

console.time('run time');
for(let i=0;i<100000;i++)
    Number(i);
console.timeEnd('run time');
//run time: 16ms

console.time('run time');
for(let i=0;i<100000;i++){
    BigInt(i);
}
console.timeEnd('run time');
//run time: 26ms

let number = Number(1);
console.time('run time');
for(let i=0;i<100000;i++)
    number + 5;
console.timeEnd('run time');
//run time: 9ms

let bigint = BigInt(1);
console.time('run time');
for(let i=0;i<100000;i++)
    bigint + 5n;
console.timeEnd('run time');
//run time: 52ms

After showing all the expensive magic of the BigInt, we didn’t use it to solve the user id’s problem, not because of the cost, but since we don’t do a numeric operation on userId’s we converted the value to string. BigInt is sitting quietly, waiting for the day all the other browsers will join. Until then, you have plenty JavaScript libraries that are doing the same.