BigInt - long arithmetic in JavaScript
- Transfer
BigInt
- A new numerical primitive data type in JavaScript that allows you to work with numbers of arbitrary precision. With BigInt
you can safely store and process large integers, even beyond the maximum safe integer value Number
. In this article we look at some examples of the use BigInt
and the new Chrome 67 features, comparing BigInt
and Number
in JavaScript.
Examples of using
Arbitrary precision integers open up many new uses for JavaScript.
BigInt helps prevent overflow errors in mathematical operations. This fact in itself makes countless possibilities available, for example, mathematical operations on large numbers are commonly used in financial technology.
Large numeric identifiers and high-precision timestamps cannot be safely represented by a data type Number
in JavaScript. This often leads to errors and forces developers to store them as strings. With BigInt
this data can be represented as numerical values.
BigInt
can be used in a possible data type implementation BigDecimal
. This will allow you to store monetary values in the form of decimal fractions without loss of accuracy during operations (for example, without a problem 0.10 + 0.20 !== 0.30
).
Earlier in JavaScript, in any of these cases, it was necessary to use libraries that emulate functionality BigInt
. When it BigInt
becomes widely available, it will be possible to abandon these dependencies in favor of a natively supported one BigInt
. This will help reduce load, parsing and compilation times, as well as increase runtime performance.
Native BigInt
is faster than popular user libraries
A polyphile BigInt
requires a library that implements the necessary functions, as well as a transpilation step, to translate the new syntax into a library API call. Babel currently supports parsing literals BigInt
, but does not know how to convert them. Therefore, we do not hope that it BigInt
will be used in production on sites requiring compatibility with a wide range of browsers. However, now that this functionality is starting to appear in browsers, you can start experimenting with BigInt, expecting more and more support for BigInt over time.
Status Quo: Number
The primitive data type Number
in JavaScript is represented by double precision floating-point numbers. The constant Number.MAX_SAFE_INTEGER
contains the maximum possible integer that can be safely increased by one. Its value is equal 2 ** 53-1
.
const max = Number.MAX_SAFE_INTEGER;
// → 9_007_199_254_740_991
Note: for readability, I group numbers using underscores as separators. The corresponding sentence would allow the use of such a notation for ordinary JavaScript numeric literals.
Its increase by one gives the expected result:
max + 1;
// → 9_007_199_254_740_992
But if we increase it by another one, Number
we will not be able to accurately save the result:
max + 2;
// → 9_007_199_254_740_992
Note that the result of the expression max + 1
will be equal to the result of the expression max + 2
. Therefore, whenever we get this particular value in JavaScript, we cannot say whether it is accurate or not. Any calculations with integers outside the safe integer range (i.e., from Number.MIN_SAFE_INTEGER
to Number.MAX_SAFE_INTEGER
) are potentially inaccurate. For this reason, we can only rely on integer values in a safe range.
New: BigInt
BigInt
- A new numerical primitive data type in JavaScript that allows you to work with numbers of arbitrary precision. With BigInt
you can safely store and process large integers, even beyond the maximum safe integer value Number
.
To create, BigInt
just add a suffix n
to the literal notation of an integer. For example, it 123
will 123n
. The global function BigInt(number)
can be used to cast the number to BigInt
. In other words, BigInt(123) === 123n
. Let's use this to solve the problems that we talked about above:
BigInt(Number.MAX_SAFE_INTEGER) + 2n;
// → 9_007_199_254_740_993n
Here is another example, with the multiplication of two numbers like Number
:
1234567890123456789 * 123;
// → 151851850485185200000
If we look at the low-order digits, 9
and 3
, it can be argued that the result of the multiplication should end with 7
(because 9 * 3 === 27
). But the result ends with a set of zeros. Something went wrong. Let's try again with BigInt
:
1234567890123456789n * 123n;
// → 151851850485185185047n
This time the result is correct.
The limits for working safely with integers are not applicable BigInt
, so BigInt
we can use long arithmetic without worrying about loss of precision.
New primitive data type
BigInt
Is a new primitive data type in the JavaScript language, so it gets its own type, which the operator can return typeof
:
typeof 123;
// → 'number'
typeof 123n;
// → 'bigint'
Since it BigInt
is an independent data type, a type number BigInt
can never be strictly equal to a type number Number
(for example, 42n !== 42
). To compare the type number BigInt
and the type number Number
, convert one of them to the type of the other before doing the comparison, or use comparison with type conversion ( ==
):
42n === BigInt(42);
// → true
42n == 42;
// → true
When cast to a boolean value (for example, in if
, when using &&
either ||
, or as a result of an expression Boolean(int)
, and so on), type numbers BigInt
behave exactly like type numbers Number
.
if (0n) {
console.log('if');
} else {
console.log('else');
}
// → logs 'else', because `0n` is falsy.
Operators
BigInt
supports most operators. Binary +
, -
, *
and **
operate as usual. /
and %
also work by rounding the result to the nearest whole if necessary. Bitwise operators |
, &
, <<
, >>
and ^
work with numbers of type BigInt
similar to the type numbers Number
when negative numbers are represented in binary form as an extra code.
(7 + 6 - 5) * 4 ** 3 / 2 % 3;
// → 1
(7n + 6n - 5n) * 4n ** 3n / 2n % 3n;
// → 1n
Unary -
be used to indicate a negative value BigInt
, for example -42n
. Unary is +
not supported because it violates the asm.js code, which expects to +x
always return either Number
or an exception.
An important point - in operations you can not mix BigInt
and Number
. This is good because any implicit conversion can lead to loss of information. Consider an example:
BigInt(Number.MAX_SAFE_INTEGER) + 2.5;
// → ??
What should the result be equal to? There is no right answer. BigInt
cannot contain fractional numbers, but Number
cannot accurately contain large numbers greater than the safe integer limit. Therefore, operations with BigInt
and Number
lead to an exception TypeError
.
The only exceptions to this rule are comparison operators, such as ===
(discussed earlier), <
and >=
since they return booleans that do not carry the risk of loss of precision.
1 + 1n;
// → TypeError
123 < 124n;
// → true
Note: since BigInt
they Number
usually do not mix, do not rewrite existing code from Number
to BigInt
. Decide which of these two types you need and use it. For newer APIs that work with potentially large integers, BigInt
a better choice. However, Number
as before, it can be used for values that are guaranteed to be in a safe range of integers.
It is also worth noting that an operator >>>
that performs an unsigned right shift does not make sense for numbers BigInt
, since they always contain a sign. Therefore, it >>>
does not work for numbers BigInt
.
API
Several new API methods have become available for BigInt
.
A global constructor BigInt
is similar to a constructor Number
: it converts its argument to BigInt
(as mentioned earlier). If the conversion fails, an exception will be thrown SyntaxError
or RangeError
.
BigInt(123);
// → 123n
BigInt(1.5);
// → RangeError
BigInt('1.5');
// → SyntaxError
There are two functions that allow you to limit values to a BigInt
specified number of significant bits, while considering the number as either signed or unsigned. BigInt.asIntN(width, value)
limits the value
type number to the BigInt
specified width
number of bits, taking into account the sign, and BigInt.asUintN(width, value)
does the same, considering the value as unsigned. For example, if you need operations on 64-bit numbers, you can use these APIs to stay in the appropriate range:
// максимально возможное значение типа `BigInt`,
// которое может быть представлено как знаковое 64-битное целое число.
const max = 2n ** (64n - 1n) - 1n;
BigInt.asIntN(64, max);
// → 9223372036854775807n
BigInt.asIntN(64, max + 1n);
// → -9223372036854775808n
// ^ значение отрицательное, так как произошло переполнение
Note that overflow occurs as soon as we pass in a type value BigInt
that exceeds the 64-bit integer range (i.e. 63 bits for the value itself and 1 bit for the character).
BigInt
allows you to accurately represent 64-bit signed and unsigned integers that are commonly used in other programming languages. Two new typed arrays, BigInt64Array
and BigUint64Array
, simplify the work with such values:
const view = new BigInt64Array(4);
// → [0n, 0n, 0n, 0n]
view.length;
// → 4
view[0];
// → 0n
view[0] = 42n;
view[0];
// → 42n
BigInt64Array
ensures that its values are within the range of possible 64-bit signed values.
// максимально возможное значение типа `BigInt`,
// которое может быть представлено как знаковое 64-битное целое число.
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
// ^ значение отрицательное, так как произошло переполнение
BigUint64Array
works the same for unsigned 64-bit values.
Have fun with BigInt
!