Using JavaScript's BigInt Data Type

seanwelshbrown

Sean Welsh Brown

Posted on August 26, 2020

Using JavaScript's BigInt Data Type

As all web developers come to know, JavaScript is a bit of an odd language with all sorts of interesting quirks in the way it compiles and functions behind the scenes.

One of the more interesting aspects of the language is the fact that it until very recently it has only used one data type for storing numbers: a Number object. This object doesn't specify between integers and floats the way many other languages do, but rather stores them all the same under the hood in double-precision 64 bit floating-point format.

However, as of the most recent versions of JavaScript's ECMA specification, there is now another method for storing and using numbers: the BigInt object.


Why do we need it?

The problem with the way JavaScript stores its numbers is that an overflow occurs when very large integers are stored, causing automatic rounding and accuracy issues.

A Number object in JavaScript can only safely store an integer between:

-9007199254740991 (-(253-1))

And:

9007199254740991 (253-1)

The overflow inaccuracy effect can be seen by console.logging the following:

console.log(9999999999999999);
// => 10000000000000000

Or:

// note the final digits
console.log(9007199254740992 === 9007199254740993);
// => true

This might seem like something that wouldn't be much of an issue in most small scale programming or web development workflows. But as JavaScript becomes more and more popular as a back end language, and as more databases and logic are written using the language, the issue of maximum safe integers became common enough for the maintainers of the language's international standard to solve the problem natively.


Here comes BigInt!

Traditionally JavaScript developers have found ways to work around issues with maximum integer sizes, such as interpolating large integers into strings and storing them that way, or by using open source libraries built my other devs.

Thanks to the introduction of BigInt however, the problem has officially been taken care of within the language itself.

To create a BigInt object, one need only append an "n" to the end of an unsafe integer, or wrap the integer in a "BigInt()" constructor, like so:

let largeNum1 = 9007199254740995n
// or
let largeNum2 = BigInt("9007199254740995")
// note that the integer is being parsed as a string,
// otherwise rounding may occur

And you're done!

Arithmetic on BigInt objects may be done using the following simple operators: +, -, *, /, **, and %.

It's worth noting that built in methods using the Math object in JavaScript will not work with BigInts, and that any arithmetic being done between a BigInt and a regular Number will not work— the Number must first be coerced into a BigInt as well.

Deeper information on the BigInt object can be found on the MDN WebDocs page: BigInt - JavaScript | MDN.


Can you give me a practical example of a BigInt use case?

Absolutely!

Let's say you're solving a problem involving Linked Lists in which you'd like to pull the values of two Lists, concatenate them into integers, add them together, and then return the sum.

Your solution might look like the following:

const addTwoNumbers = function(l1, l2) {

    // Helper method to concatenate list values
    function getNum(node) {
        let string = "";
        while (node) {
            string += node.val;
            node = node.next;
        }
        return string;
    }

    let l1Num = getNum(l1);
    let l2Num = getNum(l2);

    let sum = (parseInt(l1Num) + parseInt(l2Num));

    return sum;
};

With the following input case (imagine that each element in the array is the node value of a Linked list):

[7,2,4,3]
[5,6,4,2]

You would get the expected value of 7,243 + 5,642 = 12,885. Great!

However, with an input case like:

[2,4,3,2,4,3,2,4,3,2,4,3,2,4,3,2,4,3,2,4,3,2,4,3,2,4,3,2,4,3,2,4,3,2,4,3,2,4,3,2,4,3,2,4,3,2,4,3,2,4,3,2,4,3,2,4,3,2,4,3,9]
[5,6,4,2,4,3,2,4,3,2,4,3,2,4,3,2,4,3,2,4,3,2,4,3,2,4,3,2,4,3,2,4,3,2,4,3,2,4,3,2,4,3,2,4,3,2,4,3,2,4,3,2,4,3,2,4,3,9,9,9,9]

Those numbers are HUGE!

If we try to simply parse those concatenated strings into integers using parseInt() the way we did before, our accuracy will be completely gone and our solution will be unusable in production.

In a case like this, BigInt is the perfect tool to use. We can slightly alter our solution to use the BigInt parsing syntax instead of parseInt(), like so:

const addTwoNumbers = function(l1, l2) {

    // Helper method to concatenate list values
    function getNum(node) {
        let string = "";
        while (node) {
            string += node.val;
            node = node.next;
        }
        return string;
    }

    let l1Num = getNum(l1);
    let l2Num = getNum(l2);

    // note the difference \/ !
    let sum = (BigInt(l1Num) + BigInt(l2Num));

    return sum;
};

And now we'll have an accurately calculated total!

Some extra logic could be implemented to coerce a value back to a Number object if it's less than the maximum safe integer size, so that this solution would work for all data sizes.


If you've come this far, thanks for reading! I hope you've enjoyed learning a bit about JavaScript's BigInt data type.

Again, I highly recommend reading the MDN WebDocs page on BigInt for more information if you're interested in implementing them in a project, or learning more in the future.

💖 💪 🙅 🚩
seanwelshbrown
Sean Welsh Brown

Posted on August 26, 2020

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related