# BigInt: arbitrary-precision integers in JavaScript

Published · tagged with ECMAScript ES2020

`BigInt`s are a new numeric primitive in JavaScript that can represent integers with arbitrary precision. With `BigInt`s, you can safely store and operate on large integers even beyond the safe integer limit for `Number`s. This article walks through some use cases and explains the new functionality in Chrome 67 by comparing `BigInt`s to `Number`s in JavaScript.

## Use cases #

Arbitrary-precision integers unlock lots of new use cases for JavaScript.

`BigInt`s make it possible to correctly perform integer arithmetic without overflowing. That by itself enables countless new possibilities. Mathematical operations on large numbers are commonly used in financial technology, for example.

Large integer IDs and high-accuracy timestamps cannot safely be represented as `Number`s in JavaScript. This often leads to real-world bugs, and causes JavaScript developers to represent them as strings instead. With `BigInt`, this data can now be represented as numeric values.

`BigInt` could form the basis of an eventual `BigDecimal` implementation. This would be useful to represent sums of money with decimal precision, and to accurately operate on them (a.k.a. the `0.10 + 0.20 !== 0.30` problem).

Previously, JavaScript applications with any of these use cases had to resort to userland libraries that emulate `BigInt`-like functionality. When `BigInt` becomes widely available, such applications can drop these run-time dependencies in favor of native `BigInt`s. This helps reduce load time, parse time, and compile time, and on top of all that offers significant run-time performance improvements.

## The status quo: `Number`#

`Number`s in JavaScript are represented as double-precision floats. This means they have limited precision. The `Number.MAX_SAFE_INTEGER` constant gives the greatest possible integer that can safely be incremented. Its value is `2**53-1`.

``const max = Number.MAX_SAFE_INTEGER;// → 9_007_199_254_740_991``

Note: For readability, I’m grouping the digits in this large number per thousand, using underscores as separators. The numeric literal separators proposal enables exactly that for common JavaScript numeric literals.

Incrementing it once gives the expected result:

``max + 1;// → 9_007_199_254_740_992 ✅``

But if we increment it a second time, the result is no longer exactly representable as a JavaScript `Number`:

``max + 2;// → 9_007_199_254_740_992 ❌``

Note how `max + 1` produces the same result as `max + 2`. Whenever we get this particular value in JavaScript, there is no way to tell whether it’s accurate or not. Any calculation on integers outside the safe integer range (i.e. from `Number.MIN_SAFE_INTEGER` to `Number.MAX_SAFE_INTEGER`) potentially loses precision. For this reason, we can only rely on numeric integer values within the safe range.

## The new hotness: `BigInt`#

`BigInt`s are a new numeric primitive in JavaScript that can represent integers with arbitrary precision. With `BigInt`s, you can safely store and operate on large integers even beyond the safe integer limit for `Number`s.

To create a `BigInt`, add the `n` suffix to any integer literal. For example, `123` becomes `123n`. The global `BigInt(number)` function can be used to convert a `Number` into a `BigInt`. In other words, `BigInt(123) === 123n`. Let’s use these two techniques to solve the problem we were having earlier:

``BigInt(Number.MAX_SAFE_INTEGER) + 2n;// → 9_007_199_254_740_993n ✅``

Here’s another example, where we’re multiplying two `Number`s:

``1234567890123456789 * 123;// → 151851850485185200000 ❌``

Looking at the least significant digits, `9` and `3`, we know that the result of the multiplication should end in `7` (because `9 * 3 === 27`). However, the result ends in a bunch of zeroes. That can’t be right! Let’s try again with `BigInt`s instead:

``1234567890123456789n * 123n;// → 151851850485185185047n ✅``

This time we get the correct result.

The safe integer limits for `Number`s don’t apply to `BigInt`s. Therefore, with `BigInt` we can perform correct integer arithmetic without having to worry about losing precision.

### A new primitive #

`BigInt`s are a new primitive in the JavaScript language. As such, they get their own type that can be detected using the `typeof` operator:

``typeof 123;// → 'number'typeof 123n;// → 'bigint'``

Because `BigInt`s are a separate type, a `BigInt` is never strictly equal to a `Number`, e.g. `42n !== 42`. To compare a `BigInt` to a `Number`, convert one of them into the other’s type before doing the comparison or use abstract equality (`==`):

``42n === BigInt(42);// → true42n == 42;// → true``

When coerced into a boolean (which happens when using `if`, `&&`, `||`, or `Boolean(int)`, for example), `BigInt`s follow the same logic as `Number`s.

``if (0n) {  console.log('if');} else {  console.log('else');}// → logs 'else', because `0n` is falsy.``

### Operators #

`BigInt`s support the most common operators. Binary `+`, `-`, `*`, and `**` all work as expected. `/` and `%` work, and round towards zero as needed. Bitwise operations `|`, `&`, `<<`, `>>`, and `^` perform bitwise arithmetic assuming a two’s complement representation for negative values, just like they do for `Number`s.

``(7 + 6 - 5) * 4 ** 3 / 2 % 3;// → 1(7n + 6n - 5n) * 4n ** 3n / 2n % 3n;// → 1n``

Unary `-` can be used to denote a negative `BigInt` value, e.g. `-42n`. Unary `+` is not supported because it would break asm.js code which expects `+x` to always produce either a `Number` or an exception.

One gotcha is that it’s not allowed to mix operations between `BigInt`s and `Number`s. This is a good thing, because any implicit coercion could lose information. Consider this example:

``BigInt(Number.MAX_SAFE_INTEGER) + 2.5;// → ?? 🤔``

What should the result be? There is no good answer here. `BigInt`s can’t represent fractions, and `Number`s can’t represent `BigInt`s beyond the safe integer limit. For that reason, mixing operations between `BigInt`s and `Number`s results in a `TypeError` exception.

The only exception to this rule are comparison operators such as `===` (as discussed earlier), `<`, and `>=` – because they return booleans, there is no risk of precision loss.

``1 + 1n;// → TypeError123 < 124n;// → true``

Because `BigInt`s and `Number`s generally don’t mix, please avoid overloading or magically “upgrading” your existing code to use `BigInt`s instead of `Number`s. Decide which of these two domains to operate in, and then stick to it. For new APIs that operate on potentially large integers, `BigInt` is the best choice. `Number`s still make sense for integer values that are known to be in the safe integer range.

Another thing to note is that the `>>>` operator, which performs an unsigned right shift, does not make sense for `BigInt`s since they’re always signed. For this reason, `>>>` does not work for `BigInt`s.

### API #

Several new `BigInt`-specific APIs are available.

The global `BigInt` constructor is similar to the `Number` constructor: it converts its argument into a `BigInt` (as mentioned earlier). If the conversion fails, it throws a `SyntaxError` or `RangeError` exception.

``BigInt(123);// → 123nBigInt(1.5);// → RangeErrorBigInt('1.5');// → SyntaxError``

Two library functions enable wrapping `BigInt` values as either signed or unsigned integers, limited to a specific number of bits. `BigInt.asIntN(width, value)` wraps a `BigInt` value to a `width`-digit binary signed integer, and `BigInt.asUintN(width, value)` wraps a `BigInt` value to a `width`-digit binary unsigned integer. If you’re doing 64-bit arithmetic for example, you can use these APIs to stay within the appropriate range:

``// Highest possible BigInt value that can be represented as a// signed 64-bit integer.const max = 2n ** (64n - 1n) - 1n;BigInt.asIntN(64, max);→ 9223372036854775807nBigInt.asIntN(64, max + 1n);// → -9223372036854775808n//   ^ negative because of overflow``

Note how overflow occurs as soon as we pass a `BigInt` value exceeding the 64-bit integer range (i.e. 63 bits for the absolute numeric value + 1 bit for the sign).

`BigInt`s make it possible to accurately represent 64-bit signed and unsigned integers, which are commonly used in other programming languages. Two new typed array flavors, `BigInt64Array` and `BigUint64Array`, make it easier to efficiently represent and operate on lists of such values:

``const view = new BigInt64Array(4);// → [0n, 0n, 0n, 0n]view.length;// → 4view[0];// → 0nview[0] = 42n;view[0];// → 42n``

The `BigInt64Array` flavor ensures that its values remain within the signed 64-bit limit.

``// Highest possible BigInt value that can be represented as a// signed 64-bit integer.const max = 2n ** (64n - 1n) - 1n;view[0] = max;view[0];// → 9_223_372_036_854_775_807nview[0] = max + 1n;view[0];// → -9_223_372_036_854_775_808n//   ^ negative because of overflow``

The `BigUint64Array` flavor does the same using the unsigned 64-bit limit instead.

## Polyfilling and transpiling BigInts #

At the time of writing, `BigInt`s are only supported in Chrome. Other browsers are actively working on implementing them. But what if you want to use `BigInt` functionality today without sacrificing browser compatibility? I’m glad you asked! The answer is… interesting, to say the least.

Unlike most other modern JavaScript features, `BigInt`s cannot reasonably be transpiled down to ES5.

The `BigInt` proposal changes the behavior of operators (like `+`, `>=`, etc.) to work on `BigInt`s. These changes are impossible to polyfill directly, and they are also making it infeasible (in most cases) to transpile `BigInt` code to fallback code using Babel or similar tools. The reason is that such a transpilation would have to replace every single operator in the program with a call to some function that performs type checks on its inputs, which would incur an unacceptable run-time performance penalty. In addition, it would greatly increase the file size of any transpiled bundle, negatively impacting download, parse, and compile times.

A more feasible and future-proof solution is to write your code using the JSBI library for now. JSBI is a JavaScript port of the `BigInt` implementation in V8 and Chrome — by design, it behaves exactly like the native `BigInt` functionality. The difference is that instead of relying on syntax, it exposes an API:

``import JSBI from './jsbi.mjs';const max = JSBI.BigInt(Number.MAX_SAFE_INTEGER);const two = JSBI.BigInt('2');const result = JSBI.add(max, two);console.log(result.toString());// → '9007199254740993'``

Once `BigInt`s are natively supported in all browsers you care about, you can use `babel-plugin-transform-jsbi-to-bigint` to transpile your code to native `BigInt` code and drop the JSBI dependency. For example, the above example transpiles to:

``const max = BigInt(Number.MAX_SAFE_INTEGER);const two = 2n;const result = max + two;console.log(result);// → '9007199254740993'``

If you’re interested in how `BigInt`s work behind the scenes (e.g. how they are represented in memory, and how operations on them are performed), read our V8 blog post with implementation details.