JavaScript BigInt usage guide
- JavaScript BigInt usage guide
JavaScript BigInt usage guide
We all know that JavaScript is an ever-evolving language. Prior to the release of the 2020 version of ES2020, there was no native way to accurately represent and perform mathematical operations on numbers greater than 9,007,199,254,740,991
or 2**53 – 1
or less than -9,007,199,254,740,991
or . -(2**53 – 1)
Most developers rely on libraries such as JSBI and bignumber.js to perform calculations on very large numbers.
This situation gives rise to another data type called BigInt
. BigInt
The introduction of JavaScript brings the total number of JavaScript data types to eight. In this article, we'll learn BigInt
what is, its advantages, and how to use it correctly in JavaScript. To learn more about representing large numbers in Node.js applications, check out How to Handle Large Numbers in Node.js Applications .
What is BigInt?
BigInt
is a numeric data type that can be used to represent small and large numbers that cannot be represented by older numeric data types number
. Each BigInt
value must contain a lowercase letter after the number n
, 897n
e.g. Therefore, it is accurate to say that is 21
not strictly equal to 21n
, since the former is one number
and the latter is one bigint
.
When you perform math on any number greater than 9,007,199,254,740,991
or Number.MAX_SAFE_INTEGER
, you don't actually get an exact answer. We can see an example below:
const largeNumber = Number.MAX_SAFE_INTEGER; // or 9007199254740991
const addOne = largeNumber + 1;
console.log(addOne);
// returns 9007199254740992
const addTwo = largeNumber + 2;
console.log(addTwo);
// Also returns 9007199254740992
// Should return 9007199254740993
const addSix = largeNumber + 6;
console.log(addSix);
// returns 9007199254740996
// Should return 9007199254740997
const bb = 12n;
console.log(bb);
However, we can use BigInt
to handle these types of operations accurately. To define an integer as BigInt
, we can do any of the following: We can append to the integer or call the constructor n
on the entire numeric value or on a string containing just the integer value . BigInt()
For example the following example:
// Method 1
const sampleBigIntOne = 234n;
// Method 2
const sampleBigIntTwo = BigInt(567)
// returns 567n
const sampleBigIntThree = BigInt("123")
// returns 123n
const sampleBigIntFour = 12356789900099999999912111n
// 仍然正确
To verify that a value is BigInt
of type we can use typeof
the operator like this:
const sampleOne = 17n
console.log(typeof sampleOne)
// Expected result: "bigint"
const sampleTwo = BigInt(789);
console.log(typeof sampleTwo)
// Expected result: "bigint"
// 这是一个不推荐的写法
const sampleThree = typeof 17n === "bigint"
console.log(sampleThree)
// Expected result: true
Note : BigInt
Passing numbers in constructors is not recommended. Instead, it's better to just append n
or wrap it in a string first, as in sampleBigIntOne
and respectively sampleBigIntThree
.
BigInt usage examples
BigInt
Values are used in areas of life or computing where large numbers are of paramount importance. Here are BigInt
some key use cases for :
- Financial Calculations:
BigInt
Important in financial calculations as very large volumes of transactions and currency conversions can be handled - Cryptography and Secure Computing:
BigInt
Used in cryptography to generate very large random numbers that are very difficult to predict or crack. - Game Development: In game development, large numbers are often used to save timestamps, scores, and track progress.
BigInt
The use of ensures accurate representation of such values - Distributed Systems: Distributed systems require unique identities to perform accurately. Since
BigInt
values are infinite, they can be used to generate identifiers and keys
BigInt
It's really important in these areas because it lets developers handle huge integers, timestamps, IDs, and processes safely and accurately.
BigInt vs Number
In general, numbers in JavaScript can be used to represent integers or even floating point numbers, such as 69
and 4.125677
. It includes values between 9,007,199,254,740,991
or 2**53 – 1
and -9,007,199,254,740,991
or . These limits are available as constants, such as and -(2**53 – 1)
respectively .Number.MAX_SAFE_INTEGER
Number.MIN_SAFE_INTEGER
Actually you can do all arithmetic operations on numbers such as addition ( +
), subtraction ( -
), multiplication ( *
), division ( /
), remainder or modulo ( %
) and exponent ( **
) without any problem in this range, but BigInt
not Absolutely.
BigInt
Can be used to represent and perform operations on integers of any size, except floating point numbers or decimals, including numbers greater than 9,007,199,254,740,991
or less than . -9,007,199,254,740,991
Such 17.2n
decimal values are invalid.
In general, all arithmetic operations except ( ) give the correct mathematical answer BigInt
when used between two or more values. The division operator will not always give an exact result, so operations such as will round off the decimal part to instead of ./
5n/2n
2n
2.5n
Number type restrictions
JavaScript numeric types are written as double-precision 64-bit binary IEEE 754 values, with 52 bits used for the significand and 1 and 11 bits used for the sign and exponent. Therefore, whenever used Number
, we may face some limitations. These restrictions include:
- Rounding error and limited numerical precision
- Unable to perform exact arithmetic outside of minimum and maximum safe integers
- Cannot perform operations outside the scope of
MAX_VALUE
andMIN_VALUE
JavaScript necessarily rounds numbers because it uses 64 bits to represent floating point numbers. This means that every number in JavaScript is converted to a double-precision binary floating point number before being stored in memory. The 64-bit representation of a number is actually divided into three parts, including significant digits , offset exponent , and sign .
For example, a number like 15 would look like this when converted: 0.10000000010.1110000000000000000000000000000000000000000000000000
.
The first part of a double-precision binary floating-point number is the sign, which can be 0 or 1, representing the sign of the number. Therefore, a sign of 0 is positive and vice versa. Occupies 1 position. The second part is the offset exponent, which occupies 11 bits, and the last part is the significant digit or mantissa, which occupies 52 bits.
Some numbers give exact values, such as 1/2
, 1/4
, 1/5
, 1/8
, 1/10
and 9,007,199,254,740,991
or 2**53 – 1
to -9,007,199,254,740,991
or -(2**53 – 1)
. Some other numbers do not give exact values; instead, they are repeating decimals, such as 1/3
, 1/6
, 1/7
, 1/9
.
The problem is that some of these decimal numbers lose precision when converted to binary and vice versa. So when you take a decimal (for example 0.1
) and add it up to 0.2
, they don't add up to 0.3
. Instead, they add up to 0.30000000000000004
. More examples are below:
const sample1 = 0.2 + 0.6;
console.log(sample1); // 0.6000000000000001
const sample2 = 0.3 + 0.6;
console.log(sample2); // 0.8999999999999999
This is a huge problem when working with numbers because it introduces a lot of risk in calculations that require high precision. This problem is avoided when we use BigInt
, since it does not accept decimals.
JavaScript numbers are only accurate if you perform arithmetic operations between 9,007,199,254,740,991
or 2**53 – 1
to -9,007,199,254,740,991
or . -(2**53 – 1)
So anything you do outside of that range may lead to wrong answers, for example:
const sample3 = 9_007_199_254_740_991 + 1; // 9,007,199,254,740,992
const sample4 = 9_007_199_254_740_991 + 2; // 9,007,199,254,740,992
console.log(sample3 === sample4); // true
JavaScript also has limitations on the size of numbers it can represent. They can be represented by two built-in constants: Number.MAX_VALUE
and Number.MIN_VALUE
. They are essentially numbers: 1.7976931348623157e+308
and , respectively 5e-324
.
Whenever you try to Number.MAX_VALUE
perform addition or subtraction using , it returns 1.7976931348623157e+308
. When you try to Number.MIN_VALUE
do the same thing with , it returns the value in the following example:
const sample5 = Number.MAX_VALUE + 7;
console.log(sample5); // 1.7976931348623157e+308
const sample6 = Number.MAX_VALUE - 23;
console.log(sample6); // 1.7976931348623157e+308
const sample7 = Number.MIN_VALUE + 5;
console.log(sample7); // 5
const sample8 = Number.MIN_VALUE - 54;
console.log(sample8); // -54
When you Number.MAX_VALUE
perform multiplication ( *
) or exponentiation ( **
) on , it always returns infinity. This value is expressed as Number.POSITIVE_INFINITY
.
Only division applies Number.MAX_VALUE
, only multiplication applies Number.MIN_VALUE
. This is a serious limitation when you need to perform calculations involving very large numbers. However, BigInt
with the help of , we can perform calculations on very large or very small numbers.
Advantages of using BigInt
Now, let's dive into the advantages of using in JavaScript BigInt
. First, BigInt
make sure we don't encounter precision errors. When we work with numbers, we can work with decimals, which can lead to precision and rounding errors, as we saw in the previous section.
However, when we use BigInt
, we are not allowed to use decimals. This ensures that such errors are always avoided. Therefore, it is invalid to do the following:
const sample1 = 5.4n;
// Uncaught SyntaxError: Invalid or unexpected token
BigInt
Arbitrary precision calculations can also be performed. Unlike numbers, which lose precision when performing calculations outside the range of and Number.MAX_SAFE_INTEGER
, allows us to perform such calculations without losing any precision. As shown in the following example:Number.MIN_SAFE_INTEGER
BigInt
const sample2 = BigInt(Number.MAX_SAFE_INTEGER) + 1n
const sample3 = BigInt(Number.MAX_SAFE_INTEGER) + 2n
console.log(sample2); // 9007199254740992n
console.log(sample3); // 9007199254740993n
console.log(sample3 > sample2); // true
And using BigInt
, we can safely and accurately Number.MAX_VALUE
perform calculations on integers larger than , and because it has arbitrary precision, the size of the integers it can perform operations on is only limited by the memory available on the host computer or system. We BigInt
don't encounter the same problem when using :
const max = BigInt(Number.MAX_VALUE);
console.log(max);
// 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368n
const sample4 = max + 7;
// 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858375n
const sample5 = max - 23;
// 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858345n
Furthermore, BigInt
it allows us to place limits (set bounds) on the number of digits to be calculated so that we can define appropriate integer ranges for our program. We can achieve this using the and methods BigInt
provided on the prototype .asIntN
asUintN
Essentially, these two methods are used to wrap BigInt
values into fixed-width binary signed and unsigned integers respectively. Signed integers are used to represent negative values and vice versa.
So if we want to limit a calculation to 8-bit, 16-bit, 32-bit or 64-bit arithmetic, we can use asIntN
the method BigInt.asIntN(width, value)
to do so by calling it. In this case, width
the argument represents the required bits greater than zero, and value
the argument represents the value we want to limit within the provided width
value BigInt
.
Let's see an example:
const maximumBits = 16;
const valueOne = BigInt(32767);
const constrainedValueOne = BigInt.asIntN(maximumBits, valueOne);
console.log(constrainedValueOne); // 32767n
const valueTwo = BigInt(32768);
const constrainedValueTwo = BigInt.asIntN(maximumBits, valueTwo);
console.log(constrainedValueTwo); // -32768n
As you can see in the example above, we asIntN
created the 16-bit integer limit to be used by using . Generally speaking, 16 bits can only contain integers between to -32768n
. 32767n
So, in the first example, it returns the value as is, while in the latter, it truncates the value because it is outside the range we specified.
BigInt
Also improves the speed of JavaScript applications. Before ES2020, developers relied on libraries such as Math.js and JSBI to calculate large numbers. However, some of these packages are large and slow. This causes applications and software built with JavaScript to perform slower; however, BigInt
the advent of JavaScript can improve applications that perform heavy calculations.
How to use BigInt in JavaScript
Because BigInt
is a data type, it Number
can also be used to perform calculations, similar to . To define BigInt
a value, we can use BigInt
the identifier (i.e. n
), or use BigInt
the constructor for a string containing integers or numbers.
However, we should be wary of using the constructor to cast a number to BigInt
, as the number may lose precision even before the conversion occurs. For example:
const dontDoThis = BigInt(12345567891234567890)
console.log(dontDoThis); // 12345567891234568192n
const dontDoThisToo = BigInt(`${
12345567891234567890}`)
console.log(dontDoThis); // 12345567891234568000n
How to use BigInt
You can call BigInt
five different methods of the class:
BigInt.prototype.toString()
BigInt.prototype.valueOf()
BigInt.prototype.toLocaleString()
BigInt.asIntN()
BigInt.asUintN()
Whenever you encounter BigInt.prototype.toString()
a method like , it means that toString()
the method will be called on BigInt
the object instance (such as 5n.toString()
), while a BigInt.asUintN()
method like will BigInt
be called directly on the constructor BigInt.asIntN(maximumBits, valueTwo)
.
BigInt.prototype.toString()
First, let's take a look BigInt.prototype.toString()
. toString()
Used to BigInt
convert a value to a string. It essentially wraps it in double or single quotes BigInt
while removing the trailing ones n
. It can be used like this:
const value1 = 35n;
const sample1 = value1.toString();
console.log(sample1); // "35"
You can typeof
verify that the return value is now a string using
const value1 = 35n;
const sample1 = value1.toString();
const valueDataType = typeof sample1;
console.log(valueDataType); // "string"
Alternatively, you can also BigInt
pass the base when converting to a string. The base is actually the base you want to convert to. The radix to be passed ranges from 2 to 36. Additionally, when no base is passed, it defaults to base 10. Therefore, you can pass the base like this:
const value = 10n;
const newValueBase16 = value.toString(16);
console.log(newValueBase16); // "a"
const newValueBase5 = value.toString(5);
console.log(newValueBase5); // "20"
const newValueBase10 = value.toString(10);
console.log(newValueBase10);// "10"
BigInt.prototype.valueOf()
valueOf
Method used to obtain BigInt
the original type of the object. Look at the following example:
const value = Object(7n);
const valueOfDataType1 = typeof Object(7n);
console.log(valueOfDataType1); // "object"
// 你可以使用 .valueOf 方法来获取原始类型
const valueOfDataType2 = typeof Object(7n).valueOf();
console.log(valueOfDataType2); // "bigint"
BigInt.prototype.toLocaleString()
toLocaleString()
The method is toString()
similar to , however, it can be used to return a string in a language-specific manner or format. It accepts two parameters namely locale
and options
. It can be used like this:
const value = 23345689n;
const valueAsFormattedString = value.toLocaleString('en-US');
console.log(valueAsFormattedString); // 23,345,689
const valueAsFormattedStringWithOptions = value.toLocaleString('en-US', {
style: 'currency', currency: 'USD' })
console.log(valueAsFormattedStringWithOptions); // $23,345,689.00
BigInt.asIntN()
BigInt.asIntN()
Used to BigInt
limit a value to a set bit width while preserving the sign of the value. Syntax: BigInt.asIntN(bitWidth, BigIntValue)
. BigInt
Therefore, it is suitable when we want to preserve the sign of a value (even if we want to constrain it).
Generally speaking, bitWidth
it can be 8-bit, 16-bit, 32-bit, etc. So when we set the width to 8 bits, we're essentially saying that we're accepting a total of 256 BigInt values ranging from -128
to 128
. If the value falls within the range of values held by the bit, the value is returned unchanged; if it falls outside the range, the value equivalent to the value in the bit is returned. For example:
const bits = 8;
const value1 = 126n; // 在范围为 -128 到 128 之间
const value2 = 127n; // 在范围为 -128 到 128 之间
const value3 = 128n; // 不在范围为 -128 到 128 之间
const value4 = 129n; // 不在范围为 -128 到 128 之间
const value5 = -67n; // 在范围为 -128 到 128 之间
const result1 = BigInt.asIntN(bits, value1);
console.log(result1); //126n
const result2 = BigInt.asIntN(bits, value2);
console.log(result2); // 127n
const result3 = BigInt.asIntN(bits, value3);
console.log(result3); // -128n
const result4 = BigInt.asIntN(bits, value4);
console.log(result4); // -127n
const result5 = BigInt.asIntN(bits, value5);
console.log(result5); // -67n
BigInt.asUintN()
BigInt.asUintN()
Similar to BigInt.asIntN()
. BigInt
The main difference is that it ignores the sign on the value to be constrained , which is only constrained between zero and the maximum number of bits that can be contained. For example, 8 bits can contain 256 values; therefore, it is limited to 0 to 256, inclusive. For example:
const bits = 8;
const value1 = 254n; // 在 0 到 256 之间
const value2 = 255n; // 在 0 到 256 之间
const value3 = 256n; // 不在 0 到 256 之间
const value4 = 257n; // 不在 0 到 256 之间
const value5 = 258n; // 不在 0 到 256 之间
const result1 = BigInt.asUintN(bits, value1);
console.log(result1); // 254n
const result2 = BigInt.asUintN(bits, value2);
console.log(result2); // 255n
const result3 = BigInt.asUintN(bits, value3);
console.log(result3); // 0n
const result4 = BigInt.asUintN(bits, value4);
console.log(result4); // 1n
const result5 = BigInt.asUintN(bits, value5);
console.log(result5); // 2n
BigInt conditional statement
The conditions that apply to Number
also apply to BigInt
. This means that ||
, &&
and !
can also BigInt
operate normally with . Also, when we use if
the statement, only the value 0n
is evaluated as false
, while the other values are true
:
if(0n){
console.log('It is in if')
}else{
console.log("it is in else")
}
// "it is in else"
if(-2n){
console.log("it is in if")
}else{
console.log("it is in else")
}
// "it is in if"
if(67n){
console.log("it is in if")
}else{
console.log("it is in else")
}
// "it is in if"
Even Boolean
the function works as expected, only 0n
will evaluate to false
, like this:
const isValueNone = Boolean(0n);
console.log(isValueNone); // false
const isValueExists1 = Boolean(79n);
console.log(isValueExists1); // true
const isValueExists2 = Boolean(-65n);
console.log(isValueExists2); // true
BigInt common rules and precautions
In order to use BigInt
without errors we need to follow some rules:
BigInt
Do not usenew
the keyword when creating- Don't
Bigint
use decimals in BigInt
cast error- Do not use directly
JSON.stringify
withBigInt
- Limited built-in methods
BigInt
The division operation returns a truncated result
First, BigInt
don't use new
the keyword when creating . In JavaScript, we often use new
the keyword to initialize instances of a class. However, BigInt
this is not the case because when we use new
the keyword, it throws TypeError
:
const value = new BigInt(54);
// TypeError: BigInt is not a constructor
Also, don't Bigint
use decimals in . BigInt
It throws when we try to convert decimal to value RangeError
. Similarly, when using an implicit conversion with a decimal point BigInt
, we get a SyntaxError
:
const value = 7.8n + 9n;
// SyntaxError: Invalid or unexpected token
Additionally, you may encounter BigInt
cast errors, for example mixing Number
with BigInt
will throw TypeError
:
const result = 2n + 4;
console.log(result);
// TypeError: Cannot mix BigInt and other types, use explicit conversions
Also, using null
, undefined
and Symbol
with BigInt
will throw an error:
console.log(BigInt(null));
// TypeError: can't convert null to BigInt
console.log(BigInt(undefined));
// TypeError: can't convert undefined to BigInt
console.log(BigInt(Symbol()));
// TypeError: can't convert Symbol() to BigInt
To avoid further errors, do not use directly JSON.stringify
with BigInt
. We can't BigInt
use it directly on JSON.stringify
will throw TypeError
:
const theStringified = JSON.stringify(5n);
console.log(theStringified);
// TypeError: BigInt value can't be serialized in JSON
Instead, we can BigInt
stringify the value to implement toJSON
the method or implement a replacer
method. To implement toJSON
the method, we simply JSON.stringify()
add the following code to the program file before using the method:
BigInt.prototype.toJSON = function () {
return this.toString();
};
To replacer
implement the method using JSON.stringify()
the method, we simply add the following code to our program:
const replacer = (key, value) => {
return typeof value === "bigint" ? value.toString() : value
}
We can do this using either of the following two methods JSON.stringify()
:
// 方法一
// 使用 toJSON
// 把这个放在使用 JSON.stringify() 的地方之前
BigInt.prototype.toJSON = function () {
return this.toString();
};
const value1 = {
one: 5n, two: 667n};
const value1ToJson = JSON.stringify(value1);
console.log(value1ToJson);
// {"one":"5","two":"667"}
// 方法二
// 使用 replacer
const replacer = (key, value) => {
return typeof value === "bigint" ? value.toString() : value
}
const value2 = {
seven: 7n, twenty: 20n, five: 5n};
const value2ToJson = JSON.stringify(value2, replacer);
console.log(value2ToJson);
// {"seven":"7","twenty":"20","five":"5"}
Limitations of BigInt
BigInt
One of the limitations is that we cannot use built-in functions in JavaScript Math
. Instead, we should build the mathematical functions ourselves. Therefore, performing Math.sqrt()
an operation such as will throw TypeError
an exception:
console.log(Math.sqrt(4n))
// TypeError: can't convert BigInt to number
Additionally, BigInt
division using returns a truncated result. Because BigInt
values cannot be decimals, division operations sometimes return decimal dividends, for example 7 / 4 = 1.75
. Therefore, BigInt
the value will always be rounded to 0
. This means 7n / 4n = 1n
rather than what is expected 1.75n
.
Browser and Node.js support
v10.4
Any Node.js version since (including newer v18.16
versions) is supported BigInt
, so older versions will throw syntax errors. Again, supported by most modern browsers BigInt
. You can view details on caniuse .
As of this writing, over 94% of browsers support BigInt
constructors and their methods.
summary
In this article, we learned about BigInt
its available methods, use cases, and issues with using it. However, we should know that it Number
is also useful for daily programs such as small websites and should be used only when we are sure that we are dealing with large numbers BigInt
.
thanks for reading. I hope you enjoyed this article and if you have any questions please be sure to leave a comment.