SoFunction
Updated on 2025-03-08

Detailed explanation of BigInt for numerical calculations exceeding JavaScript's secure integer limit

The basic data class Number in JavaScript is a double-precision floating point number. The maximum security range it can represent is plus or minus 9007199254740991, that is, minus one to the power of 53 of 2. Enter Number.MAX_SAFE_INTEGER and Number.MIN_SAFE_INTEGER respectively in the browser console to view the corresponding maximum/small value.

const max = Number.MAX_SAFE_INTEGER;
  // → 9_007_199_254_740_991
  // Notice:For easy reading,I use an underscore as a separator to group these numbers into thousand digits。Number text separator proposal for ordinaryJavaScriptUse digital text correctly。

Adding this maximum value by one can get the expected result:

max + 1;
// → 9_007_199_254_740_992 ✅

However, if we increase it again, the result can no longer be fully represented as a JavaScript Number:

max + 2;
// → 9_007_199_254_740_992 ❌

We will find that max+1 and max+2 have the same results. As long as we get this specific value in JavaScript, we cannot tell whether it is accurate. Any calculation of integers outside the range of safe integers (i.e., from Number.MIN_SAFE_INTEGER to Number.MAX_SAFE_INTEGER) may lose precision. For this reason, we can only rely on numeric integer values ​​in the safe range.

BigInt

BigInt is a new primitive type in JavaScript that can represent integers with any precision. With BigInt, you can safely store and manipulate large integers even if the JavaScript Number's secure integer limit is exceeded.

Chrome 67+ has started to support BigInt, and all demos in this article are based on Chrome 67.

To create a BigInt, add n suffix after the number, for example, 123 becomes 123n. The global BigInt(number) function can be used to convert Number to BigInt. In other words, BigInt(123) === 123n. Let's use these two techniques to solve the problems we encountered before:

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

We multiply two Numbers:

1234567890123456789 * 123;
// → 151851850485185200000 ❌

Looking at the above two numbers, the end is 9 and 3, 9*3=27, but the end is 000, which is obviously wrong. Let's use BigInt instead:

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

This time we got the right result.

The safe integer limit for Number does not apply to BigInt. Therefore, BigInt we can perform correct integer operations without worrying about losing precision.

BigInt is a primitive type in the JavaScript language. Therefore, this type can be detected using the typeof operator:

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

Because BigInts is a separate type, a BigInt will never equal a Number, for example 42n !== 42. To compare a BigInt and a Number, convert one of them to the other type before comparison or use abstract equal(==):

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

When cast to a Boolean (using if, &&, ||, or Boolean(int)), BigInt converts with the same logic as Number.

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

Operators

BigInt supports the most common operators, the binary operators +, -, *, **, /, % all work normally, and bitwise operations |, &, <<, >> are the same as Number.

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

Unary operator - can be used to represent a negative value BigInt, such as -42n. Unary+ is not supported because it will break the code, and in +x always throws exceptions.

Another problem is that mixing operations between BigInt and Number is not allowed. Take a look at this example:

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

What should be the result? There is no good answer here. BigInt cannot represent decimals, and Number cannot represent numbers that BigInt exceeds the safe integer limit. Therefore, a mixing operation between BigInt and Number will cause a TypeError exception.

The only exception to this rule is comparison operators such as === (as mentioned before) <, and >=- Since they return boolean values, there is no risk of precision loss.

1 + 1n;
// → TypeError
123 < 124n;
// → true

API

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

BigInt(123);
// → 123n
BigInt(1.5);
// → RangeError
BigInt('1.5');
// → SyntaxError

Two library functions enable encapsulating BigInt values ​​as signed or unsigned integers, limited to specific digits. (width, value) wraps a BigInt value as a width-digit binary signed integer, and wraps a BigInt value as a width-digit binary unsigned integer. For example, if you are performing 64-bit arithmetic, you can use these APIs to maintain the proper scope:

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

Note that as long as we pass a value of BigInt that exceeds the 64-bit integer range (e.g., absolute value is 63-bit + symbol is 1-bit), overflow will occur.

BigInt can accurately represent 64-bit signed and unsigned integers, which are commonly used in other programming languages. Two new types of array styles, BigInt64Array and BigUint64Array are easier to represent and manipulate lists of these values ​​efficiently:

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

BigInt64Array ensures that its value is 64-bit signed.

// 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_807n
view[0] = max + 1n;
view[0];
// → -9_223_372_036_854_775_808n
//  ^ negative because of overflow

BigUint64Array ensures that these values ​​are 64-bit unsigned.