Home | Send Feedback

BigInt, arbitrary precision integers in JavaScript

Published: 3. September 2018  •  javascript

BigInt is a new JavaScript primitive that can represent integers with arbitrary precision, unlike the Number primitive that can only safely store integer values up to 2^53. BigInts allow an application to work with numbers that do not fit into the Number range. An application never has to worry about overflow errors when it performs integer arithmetic with BigInt.

For example, with a BigInt, an application can represent the following number with 949 digits.

1981736304199322413033155801017007660571991838127382513183869502655132945955410381978047930657291929328288215
0606361136148620102904441975725292592628559967812484675646892511846918611620611557168971934110225307415064349
8363325492215252179814608914978848514521148286358193185975317403169331562554772751601328737118172997857019565
4412014954799861023018894168724656416695879451586197508165521397945043501171786040218486867988008752171425034
1196327155439333794638153206641651301969127462864035534874455812703397724619751741504170473595816399761522130
0466851042618062726302177752344216671603543132982051584234733113584510328373733271849956633295642753394057746
2921547661536249039107602105847940990310966177991999680511207851623398036926020603299101758654216200328207217
0802170818128446172076808512000167514374118804496410628196099710910975151856288299881083251368933286107688452
87219627992142295849162918727812831234961883883469929500772200319747415930309n

One of the applications that depend on big number arithmetic is cryptography.
If you use a back end language that supports a 64-bit datatype like Java with long, BigInts allow a JavaScript application to safely represent these values on the client (for example, primary keys, timestamps in nanoseconds).

Implementations and Standard

BigInt is part of the ES2020 standard. Chrome supports it since version 67 and Firefox since version 68.

Version 7 of Babel supports BigInt and the 3.2 version of TypeScript introduces BigInts to TypeScript.

Syntax

To differentiate a BigInt from a Number you have to append the letter n to the end of the number. For example, 156 is a Number, 156n is a BigInt.

You can convert a Number to a BigInt with the global BigInt(number) function.

const aBigInt = 11n;

const aNumber = 156;
const aBigInt = BigInt(aNumber);
aBigInt === 156n // true

BigInts can also be written with binary, octal or hexadecimal notation

const h = 0x100n;
// h === 256n

const o = 0o064n;
// o === 52n

const b = 0b10101010101n;
// b === 1365n

When used as a boolean, BigInt follows the same rule as a Number. 0n is falsy.

if (0n) {
  console.log('truthy');
} else {
  console.log('falsy');
}
// prints "falsy"

The typeof operator used with a BigInt returns "bigint"

typeof 156;
// "number"
typeof 156n;
// "bigint"

Operators

BigInts support the same operators that you can use with Numbers and work as expected.

20 - 3 * 4 ** 2 / (3 % 4) === 4
20n - 3n * 4n ** 2n / (3n % 4n) === 4n

/ and % round towards zero

6n / 7n === 0n
7n / 6n === 1n

Most bitwise operators also work like they do with Numbers

128 << 3
// 1024
128n << 3n
// 1024n

Not supported is the >>> operator, because BigInts are signed. To get an unsigned shift, pass in a positive BigInt to >>

128n >>> 3n
// Uncaught TypeError: BigInts have no unsigned right shift, use >> instead

The Unary - operator can be used for denoting a negative BigInt

const a = -156n

The unary + operator is not supported with BigInts, because it would cause a problem with asm.js which expects the unary + operator always to return a Number.

const b = +6
b === 6

const c = +6n
// Uncaught TypeError: Cannot convert a BigInt value to a number

There is one limitation you need to be aware of. You cannot mix Number and BigInt. The JavaScript engine throws an exception if you try to do that.

const d = 45 + 10n
// Uncaught TypeError: Cannot mix BigInt and other types, use explicit conversions

The JavaScript engine cannot implicitly convert Number and BigInt because BigInt does not support fractions, and Number can only represent integers safely up to 2^53.

Conversion to and from Number

You can always explicitly convert with the global functions BigInt() and Number()

BigInt(45) + 10n === 55n
45 + Number(10n) === 55

parseFloat() and parseInt() can also convert a BigInt to a Number.

const a = parseFloat(100n);
// a === 100

const b = parseInt(101n);
// b === 101

You need to be cautious with conversions to Number. If you convert a BigInt that is outside the safe integer range (2^53), Number(), parseFloat(), and parseInt() do not throw an error, but the Number they return is not able to represent the integer correctly.
If the BigInt is greater than Number.MAX_VALUE these three methods return Infinity.

const bi = 2n**56n;
// bi === 72057594037927936n

const n = Number(72057594037927936n);
// n === 72057594037927940  


const large = 2n*10n**309n;
Number(large) === Infinity;
parseFloat(large) === Infinity;

Comparisons

As mentioned before, you can't mix Numbers and BigInts. There is one exception to this rule: comparison operators. The precision does not matter here because these operators resolve to a boolean value.

10 == 10n
// true

10 === 10n
// false

9 < 10n
// true

9n > 10
// false

Conversion to and from strings

The global BigInt() function cannot only convert Numbers to BigInts but also strings to BigInts

BigInt(10) === 10n;
BigInt('100') === 100n;

The function throws an exception if it cannot convert the provided value into a BigInt.

BigInt(23.5)
// RangeError: The number 23.5 cannot be converted to a BigInt because it is not an integer

BigInt('23.5')
// SyntaxError: Cannot convert 23.5 to a BigInt

BigInts also have a toString() method that convert the number to a string

const s = 100n.toString();
// s === "100"

The + operator can be used to concatenate strings and BigInts. Same behaviour that we already know when concatenating strings and Numbers.

const s1 = 'Amount: ' + 1;
// s1 === "Amount: 1"

const s2 = 'Amount: ' + 1n;
// s2 === "Amount: 1"

You can also use BigInts in template literals. Same behaviour that you already know from Numbers.

const value = 123n;
const s3 = `Amount: ${value}`
// s3 === "Amount: 123"

JSON

JSON does not support BigInt. If you try to convert a JavaScript object with a BigInt field to JSON the JavaScript VM throws an exception.

const obj = {name: 'test', value: 100n};
const j = JSON.stringify(obj);
// TypeError: Do not know how to serialize a BigInt

As a workaround, you can convert BigInts to either Numbers (if they fit into the supported range) or to a string. JSON.stringify() supports a second parameter called a replacer, which we can use for writing a generic function that converts all BigInts to strings.

const j = JSON.stringify(obj, (key, value) => {
    if (typeof value === 'bigint') {
        return value.toString() + 'n';
    } else {
        return value;
    }
});

// j === "{"name":"test","value":"100n"}"

To parse such a JSON, we can pass a function as the second argument (reviver) to the JSON.parse() method. This function is called for each key/value pair. It first checks if the value is a string, contains only digits and ends with the letter n. If true, it passes the string to the BigInt constructor else it returns the original value.

const obj2 = JSON.parse(j, (key, value) => {
  if (typeof value === 'string' && /^\d+n$/.test(value)) {
    return BigInt(value.slice(0, -1));
  }
  return value;
});

// obj2 --> {name: "test", value: 100n}

Math

For Numbers, the JavaScript VM provides a built-in Math object that has properties and methods for mathematical constants and functions. For BigInts there is no such object implemented. I guess a lot of the provided Math methods don't make sense for a number type without fraction, but there are a few methods that could be useful even for BigInts. Fortunately, these are not that complicated to implement. Here a very trivial implementation of max, min, sign, abs, and sqrt.

class BigIntMath {

    static max(...values) {
        if (values.length === 0) {
            return null;
        }

        if (values.length === 1) {
            return values[0];
        }

        let max = values[0];
        for (let i = 1; i < values.length; i++) {
            if (values[i] > max) {
                max = values[i];
            }
        }
        return max;
    }

    static min(...values) {
        if (values.length === 0) {
            return null;
        }

        if (values.length === 1) {
            return values[0];
        }

        let min = values[0];
        for (let i = 1; i < values.length; i++) {
            if (values[i] < min) {
                min = values[i];
            }
        }
        return min;
    }

    static sign(value) {
        if (value > 0n) {
            return 1n;
        }
        if (value < 0n) {
            return -1n;
        }
        return 0n;
    }

    static abs(value) {
        if (this.sign(value) === -1n) {
            return -value;
        }
        return value;
    }

    // https://stackoverflow.com/questions/53683995/javascript-big-integer-square-root/58863398#58863398
    static rootNth(value, k = 2n) {
        if (value < 0n) {
            throw 'negative number is not supported'
        }

        let o = 0;
        let x = value;
        let limit = 100;

        while (x ** k !== k && x !== o && --limit) {
            o = x;
            x = ((k - 1n) * x + value / x ** (k - 1n)) / k;
        }

        return x;
    }

    static sqrt(value) {
        return BigIntMath.rootNth(value);
    }

}

Usage:

const min = BigIntMath.min(1n,2n,3n,4n); // min == 1n
const max = BigIntMath.max(1n,2n,3n,4n); // min == 4n
const sign = BigIntMath.sign(-11n); // sign === -1n
const abs = BigIntMath.abs(-11n); // abs === 11n
const sqrt = BigIntMath.sqrt(81n); // sqrt === 9n

There is no need for a Math.pow() implementation, we can use the exponentiation operator introduced in ECMAScript 2016.

const a = Math.pow(3,4);
// a === 81

const b = 3n ** 4n;
// b === 81n

Polyfills

Because BigInt is a primitive, there is no way for a polyfill to implement the n syntax and add BigInt support to the built-in operators in a browser that doesn't have a BigInt implementation yet or in an older browser that never gets BigInt support.

But there are Big Integer libraries that you can use today. The syntax looks different and is not that convenient as the built-in implementation. The performance is also going to be slower than the native implementation, because all these libraries have to run their calculations in JavaScript.

Google published a chart on its BigInt blog post that compares the native implementation with a few Big Integer libraries: https://v8.dev/features/bigint


The following example shows you how you can do big integers arithmetic with the bn.js library.

You add the library like any other JavaScript library

npm install bn.js

Then import it, create BN instances from strings or numbers, and then use the provided methods.

import BN from 'bn.js';

const a = new BN('156');
const b = new BN(200);

const result = a.add(b);
// result.toString() === "356"


const b1 = new BN('90071992547409910000');
const b2 = new BN('29839283291982398238');
const r = b1.mul(b2);
// r.toString() === "2687683702295491619562538437047738580000"

The bn.js library provides a wide variety of methods. See the documentation on the GitHub project page: https://github.com/indutny/bn.js/

Many other libraries introduce big integer arithmetic to Node.js and the browser.