Original text: developers.google.com/web/updates… This is an introduction to BigInt, a new data type, published by Google developers. (Please note the source for reprint)

BigInt: an arbitrary precision integer in JavaScript

BigInt is a new numeric primitive type in JavaScript that can be used to represent integer values of arbitrary precision. With BigInt, we can safely store and manipulate integer values, even those outside the “safe integer” range of Number. This article will cover some of its usage scenarios and introduce some of the new features introduced in Chrome 67 by comparing Number.

Usage scenarios

If JavaScript had integers with arbitrary precision, it would unlock a lot of application scenarios.

BigInt performs integer arithmetic correctly for us without overflow problems. This alone opens up countless new possibilities. Especially in the field of financial technology, large numerical math is often used, such as:

In JavaScript, Number cannot safely be used to represent “large integer ID” and “high precision timestamp”. This often leads to real world problems, and developers end up being forced to use strings instead. With BigInt, the data can be represented as numeric values.

BigInt can also be used as an implementation of BigDecimal. This is useful for sums with decimals (the familiar 0.10 + 0.20! == 0.30 question).

Until then, JavaScript applications involved in these scenarios had to rely on user-implemented third-party libraries that could mimic BigInt’s functionality. With BigInt now widely supported, such applications can discard these runtime dependencies. This helped us reduce load time, parsing time, and compile time, and it also gave us a significant run-time performance improvement.

As you can see from the chart above, Chrome’s native BigInt performs better than popular third-party libraries.

If you are “Polyfilling” BigInt, you need a runtime library that does the same thing and a transformation step that translates the new syntax into calls to the library’s API. Babel already supports BigInt literal parsing through a plug-in, but does not yet support syntax conversion. Therefore, we do not recommend putting BigInt into production environments where cross-browser compatibility is widely supported at this time. It’s still early days, but its functionality is already being laid out across browsers. It is believed that the moment of widespread support for BigInt should come soon.

The present situation of the Number

Number is used in JavaScript to represent a value of the double precision floating point type. That means it has a precision limitation. The value of the number. MAX_SAFE_INTEGER constant represents the largest integer value that can be safely incremented by 1, and its value is 2**53-1. (The two * s here are not errors, but a syntax for the power)

const max = Number.MAX_SAFE_INTEGER;
/ / - > 9 _007_199_254_740_991
Copy the code

Note: for readability, large values are separated by thousands with an underscore as a delimiter.

If we increment it by one, we get:

max + 1;
/ / - > 9 _007_199_254_740_992 ✅
Copy the code

But if we increment it by 1 again, the theoretical result is no longer accurate:

max + 2;
/ / - > 9 _007_199_254_740_992 ❌
Copy the code

You should notice that the above two pieces of code give the same result. So every time we get a value like this in JavaScript, we have no way of knowing if it’s correct. All calculations outside the safe integer range can be inaccurate. So we can only trust integer values that are in the safe range.

New star:BigInt

BigInt is a new numeric primitive data type in JavaScript that can be used to represent integer values of arbitrary precision.

To create a BigInt, we simply append the n suffix to any integer literal. For example, let’s write 123 as 123n. The global BigInt(number) can be used to convert a number to a BigInt, meaning that BigInt(123) === 123n. Now let me use these two points to solve the problem we mentioned earlier:

BigInt(Number.MAX_SAFE_INTEGER) +2n;
/ / - > 9 _007_199_254_740_993n ✅
Copy the code

Here is another example where we multiply two numbers:

1234567890123456789 * 123;
/ / - 151851850485185200000 ❌
Copy the code

In this case, the mantissa of the two numbers is 9 and 3, so the result should have a mantissa of 7 (because 9 * 3 === 27), but we get a mantissa of 0, which is obviously not correct! Let’s try using BigInt instead:

1234567890123456789n * 123n;
/ / - > 151851850485185185047 n ✅
Copy the code

This time we got the right result.

Because BigInt doesn’t have the “safe integer” range of Number, we can do arithmetic on it without worrying about losing precision.

A new primitive type

BigInt is a new primitive data type in JavaScript, so it also has its own type, which we can detect using the Typeof operator:

typeof 123;
/ / - > 'number'
typeof 123n;
/ / - > 'bigint'
Copy the code

Because BigInt is a separate data type, BigInt and Number of the same value are not “strictly equal”, i.e. 42N! = = 42. To compare them, either cast one to the other’s data type or use the abstract equality operator (==) :

42n === BigInt(42);
/ / to true
42n == 42;
/ / to true
Copy the code

In needs to be converted to Boolean value scenarios (for example, if, &&, | |, Boolean (int)), BigInt follow the same rules and Number.

if (0n) {
  console.log('if');
} else {
  console.log('else');
}
// → logs 'else', because `0n` is falsy.
Copy the code

The operator

BigInt supports binary operators such as +, -, and **. Operators like/and % are also rounded automatically if necessary; If it is a binary operator |, &, < <, > >, ^, when executed and as regards as the negative Number in the form of “two’s complement”.

(7 + 6 - 5) * 4支那3 / 2 % 3;
/ / - 1
(7n + 6n - 5n) * 4n ** 3n / 2n % 3n;
/ / - 1 n
Copy the code

Unary operator – can be used to mark a negative BigInt value, e.g. -42; The unary + operator is not available because +x always returns a Number or an exception in asM.js, so it might break asM.js code.

One important point to note is that BigInt and Number cannot be mixed. This is actually a good thing, because any implicit conversion can lose information. Take this example:

BigInt(Number.MAX_SAFE_INTEGER) + 2.5;
/ / - >?? 🤔
Copy the code

What should the result be? We don’t have a good answer yet. Because BigInt has no decimal part, and Number cannot represent values outside the safe integer range; Therefore, mixing them will directly report TypeError.

The exceptions to this rule are the ones we mentioned earlier, such as ===, <, >=, etc., because their results are Booleans and there is no risk of loss of accuracy.

1 + 1n;
/ / > TypeError
123 < 124n;
/ / to true
Copy the code

Note: Because BigInt and Number do not support mixed operations, avoid overwriting with BigInt or accidentally “upgrading” existing code. Please confirm the scope of both applications before starting. BigInt is a great choice for later API additions that require large numeric manipulation. And Number is still useful for knowing integer values that are explicitly in the safe range.

Another point to note is the >>> operator, which performs an unsigned shift to the right, which really doesn’t make any sense for BigInt because it’s always signed. Therefore, BigInt does not support >>> operations.

This API

There are several apis associated with BigInt.

One of these is the global BigInt constructor, which does the same thing as the Number constructor: converts the received argument to a BigInt (as mentioned earlier); If the conversion fails, a SyntaxError or RangeError exception is thrown.

BigInt(123);
/ / - > 123 n
BigInt(1.5);
/ / > RangeError
BigInt('1.5');
/ / > SyntaxError
Copy the code

In addition, to wrap a BigInt as a “signed” or “unsigned” value, we have two functions to use. One is bigint.asintn (width, value), which wraps a BigInt value into a binary “signed integer” value the size of a width value. The other is bigint.asuintn (width, value), which wraps a BigInt into a binary “unsigned integer” of the size and length of the width value. If you want to do 64-bit arithmetic now, you can use these two apis to ensure that the arithmetic is within the desired range:

// The maximum number of signed 64-bit integers
const max = 2n ** (64n - 1n) - 1n;
BigInt.asIntN(64, max); -9223372036854775807n
BigInt.asIntN(64, max + 1n);
/ / n - > - 9223372036854775808
// ^ This is negative because of overflow
Copy the code

Note in the above code that overflow occurs whenever we pass a parameter that exceeds the maximum value of a 64-bit integer.

Also, “signed 64-bit integers” and “unsigned 64-bit integers” are common types in other languages, and BigInt now represents both types exactly, as you can see from the example above. In addition, two typed arrays are provided: BigInt64Array and BigIntUint64Array can be used to efficiently represent and easily manipulate both types of tabulated data:

const view = new BigInt64Array(4);
// → [0n, 0n, 0n, 0n]
view.length;
/ / - 4
view[0];
/ / - > 0 n
view[0] = 42n;
view[0];
/ / - 42 n
Copy the code

BigInt64Array also ensures that every element in it is a “signed 64-bit integer” :

// The maximum number of signed 64-bit integers
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
// ^ This is negative because of overflow
Copy the code

The BigUint64Array also ensures a limit of “unsigned 64-bit”.

Thanks for watching and have fun with BigInt!

Acknowledgments: Many thanks to Daniel Ehrenberg, the leader of the BigInt specification, for proofreading this article.