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.710^308 to 1.710^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.
More great articles from LogRocket:
- Don't miss a moment with The Replay, a curated newsletter from LogRocket
- Learn how LogRocket's Galileo cuts through the noise to proactively resolve issues in your app
- Use React's useEffect to optimize your application's performance
- Switch between multiple versions of Node
- Discover how to animate your React app with AnimXYZ
- Explore Tauri, a new framework for building binaries
- Advisory boards aren’t just for executives. Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
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 more easily 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.