In the past, developers struggled with programs that made use of extremely large numbers because the JavaScript `Number`

primitive had a limit on the minimum and maximum values it could represent correctly.

Consequently, this led to many unstable workarounds, such as converting large values to strings, or outsourcing work to third-party vendors, which led to bugs and/or large build sizes.

But with the introduction of the `BigInt`

primitive to the ECMAScript specification, developers no longer need to rely on fragile workarounds or third-party libraries. Instead, `BigInt`

allows them to safely work with numbers beyond the limits of the `Number`

primitive.

In this article, we’ll learn what prompted the addition of the `BigInt`

primitive into the ECMAScript specification, how `BigInt`

solves the problem, and, lastly, we’ll learn how to get started with `BigInt`

.

## Why use `BigInt`

?

Because there are many limitations using `Number`

and data types in JavaScript.

In JS, the `Number`

data type represents all numbers in JavaScript as double-precision-floating point number using the format defined by the IEEE 754, meaning that numbers in JavaScript are represented as double precision floats, or doubles for short.

Conventionally, because the `Number`

primitive represents all numbers as doubles, they are always allocated 64 bits of memory. With it, numbers ranging from -1.7*10^308 to 1.7*10^308 can be represented and stored in variables.

Unfortunately, we cannot reliably work with all the numbers within this range as most of them are *unsafe integers* — numerical representations that reference more than one real world number.

This occurs because even though a specific real world number cannot be exactly represented according to the IEEE 754 format, it will be rounded using one of the standard “rounding modes” in order to force the number to adhere to the format.

The result? The computer will round certain numbers in a way that makes them equal to other numbers that do not need to be rounded in order to ensure it follows the format.

Essentially, these unsafe integers do not have their own private representation; instead, they erroneously share the representation of other real world numbers that do not need to undergo rounding to conform to the format.

Here’s an example:

// JS defines the maximum safe interger as a constant Number.MAX_SAFE_INTEGR const safeInt = Number.MAX_SAFE_INTEGER // -> 9_007_199_254_740_991 // If we add one we get safeInt + 1 // -> 9_007_199_254_740_992 ✅ // If we add 2... safeInt + 2 // -> 9_007_199_254_740_992 🤦🏾♂️ // Therefore 9_007_199_254_740_992 or (2^53) is deemed unsafe because two real world numbers 9_007_199_254_740_992 and 9_007_199_254_740_993 are represented through it. That is why safeInt + 1 === safeInt + 2 // -> true

So, what does this mean? Using numbers greater than or smaller than `Number.MAX_SAFE_INTEGR`

or `Number.MIN_SAFE_INTEGER`

is guaranteed to cause bugs.

Many of us may need not worry about this, as the range of numerical quantities we use is well within the limits of `Number.MAX_SAFE_INTEGR`

and `Number.MIN_SAFE_INTEGR`

.

Nevertheless, some developers have to work beyond these boundaries, such as those who work in finance or find themselves constantly performing calculations with incredibly large numbers.

Fortunately, there is a solution: `BigInt`

.

## What is `BigInt`

?

`BigInt`

is a relatively new numeric primitive/integer type in JavaScript. It was created to solve the limitations people ran into with the `Number`

primitive and safe integer restrictions.

`BigInt`

represents numbers with arbitrary precision, meaning it uses as much space as needed to store and represent large numbers instead of forcefully trying to represent them using a fixed amount of memory like the `Number`

integer type does.

You can think of `BigInt`

and `Number`

like static and dynamic arrays. `BigInt`

will use up more space if it needs to when representing a large number, like a dynamic array. But `Number`

will only make use of the fixed memory initially allotted to it to represent numbers, like a static array.

`BigInt`

gives us the ability to work with large numbers without having to worry about potentially losing precision (digits) or weird representation issues that hamper accuracy and create bugs.

## Getting started with `BigInt`

To create a `BigInt`

, simply add `n`

at the end of any integer literal. Notice that doing so with decimals/floats will throw a `RangeError`

:

// This is alright const bigInteger = 1000000000000000000000000n // This will throw a RangeError const bigInteger = 1.5n // -> RangeError // You can also create a BigInt with negative numbers const negativeBigIntInteger = -1111111n // -> -1111111n

Alternatively, you can also use the global `BigInt`

function passing an integer literal as an argument.

// This is also alright const bigIntefer = BigInt(1000000000000000000000000000000000) // This will still throw a RangeError const bigInteger = BigInt(1.5)

`BigInt`

literals can also be instantiated using strings, binary, hexadecimal, or octal notation.

// Strings BigInt("1111111111111111111111111111111111111") // -> 1111111111111111111111111111111111111n // Binary BigInt(0b100000000000000000000000000000000000000000000000000000000000000000000001111111) // -> 151115727451828646838272n // Hexadecimal BigInt(0xfffffffffffffffffffffffffffffffffff9fff9fffffffffffffffff) // -> 95780971304118053647396689196894323976171195136475136n // Octal BigInt(0o40000000000000000000000000000000000000000011112444) // -> 713623846352979940529142984724747568191373312n

You cannot compare a `BigInt`

and a regular `Number`

using strict equality (`===`

) because `BigInt`

is a primitive on its own.

Therefore, calling `typeof`

on a `BigInt`

literal will return `"bigint"`

instead of `"number"`

, causing strict comparisons between them to return false.

const a = 111111n const b = 111111 a === b // -> false

However, if you were to use abstract equality (`==`

), then comparing a `BigInt`

literal with a value of `11n`

and a `Number`

literal with a value of `11`

will return `true`

because both literals are of the same value.

const a = 11n const b = 11 a == b // -> true

All arithmetic operations (`+`

, `-`

, `/`

, `*`

) can be performed on `BigInt`

literals, with the exception of unary plus. For example, you can’t write `+11n`

like you would `+11`

.

On the other hand, you can increment `BigInt`

literals with `++`

and decrement them with `--`

.

Moreover, arithmetic with `BigInt`

literals must be between `BigInt`

literals. A `Number`

literal cannot be an operand in an arithmetic operation involving a `BigInt`

. Trying to do so will result in a `TypeError`

.

// We can do this 11n + 12n // -> 23n // But we can't do this 11n + 12 // -> TypeError

Additionally, because `BigInt`

arithmetic returns a `BigInt`

, the return value will always be an integer of type `"bigint"`

.

5n / 3n // -> 1n 19n / 2n // -> 9n

`BigInt`

literals greater than `0n`

are all coerced to `true`

. While `0n`

, is coerced to `false`

.

if (5n) { // Code block will run } if (0n) { // Code block will not run }

Also, `BigInt(true)`

will return `1n`

.

BigInt(true) === 1n // -> true

The `BigInt`

global function contains two static methods that will restrict a `BigInt`

representation to using a number of bits that is specified as the first parameter of both methods.

Once `BigInt`

is within the specified space limit, it will be returned as either a signed or unsigned integer, depending on the method used.

The first method, `BigInt.asIntN(bits, <bigInt-number>)`

, returns the `<bigInt-number>`

as a signed integer.

The second method, `BigInt.asUintN(bits, <bigInt-number>)`

returns the `<bigInt-number>`

as an unsigned integer.

These methods may be useful for explicit memory management. We know that, by default, `BigInt`

uses as many bits as necessary to represent a number, but, if you are strapped for memory and know the range for the numerical values for your application, then these methods will be useful.

// For representing BigInt numbers as signed integers BigInt.asIntN(bits, <BigInt>) // For representing BigInt numbers as unsigned integers BigInt.asUintN(bits, <BigInt>)

## Conclusion

After reading this article, you hopefully have a deeper understanding of what `BigInt`

is, the problems it solves, and how to use it.

Thank you for reading!

## LogRocket: Debug JavaScript errors easier by understanding the context

Debugging code is always a tedious task. But the more you understand your errors the easier it is to fix them.

LogRocket allows you to understand these errors in new and unique ways. Our frontend monitoring solution tracks user engagement with your JavaScript frontends to give you the ability to find out exactly what the user did that led to an error.

LogRocket records console logs, page load times, stacktraces, slow network requests/responses with headers + bodies, browser metadata, and custom logs. Understanding the impact of your JavaScript code will never be easier!

Try it for free.