πŸ“œ ⬆️ ⬇️

BigInt - long arithmetic in javascript

BigInt is a new numeric primitive data type in JavaScript that allows you to work with arbitrary precision numbers. With BigInt you can safely store and process large integers even beyond the maximum safe integer value Number . In this article, we will look at some examples of using BigInt and the new features of Chrome 67, comparing BigInt and Number in JavaScript.


Examples of using


Arbitrary precision integers open up many new uses for JavaScript.


BigInt allows you to prevent overflow errors during mathematical operations. This fact in itself makes countless possibilities available, for example, mathematical operations on large numbers are commonly used in financial technologies.


Large numeric identifiers and high-precision time stamps cannot be safely represented by the Number data type in JavaScript. This often leads to errors and forces developers to store them as strings. With BigInt this data can be represented as numeric values.


BigInt can be used in a possible implementation of the BigDecimal data type. This will allow storing monetary values ​​as decimal fractions without loss of accuracy when performing operations (for example, without a 0.10 + 0.20 !== 0.30 problem 0.10 + 0.20 !== 0.30 ).


Previously, in JavaScript, in any of these cases, you had to use libraries that emulate the BigInt functionality. When BigInt becomes widely available, it will be possible to abandon these dependencies in favor of natively supported BigInt . This will help reduce download time, parsing and compilation, as well as increase runtime performance.


https://habrastorage.org/webt/ep/l8/hc/epl8hchee6j7hskpzq0g_ivp1he.png
Native BigInt runs faster than popular custom libraries


For the BigInt BigInt , a library is required that implements the necessary functions, as well as a transfiguration step, to translate the new syntax into a library API call. Babel currently supports BigInt parsing literals, but cannot convert them. Therefore, we do not hope that BigInt will be used in production on sites that require 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 with time more and more support for BigInt.


Status Quo: Number


The primitive Number data type in JavaScript is represented by double-precision floating-point numbers. The constant Number.MAX_SAFE_INTEGER contains the largest possible integer that can be safely incremented by one. Its value is 2 ** 53-1 .


 const max = Number.MAX_SAFE_INTEGER; // β†’ 9_007_199_254_740_991 

Note: for readability, I group numbers using underscores as delimiters. The corresponding sentence will allow the use of such an entry for regular numeric JavaScript literals.


Increasing it by one gives the expected result:


 max + 1; // β†’ 9_007_199_254_740_992 

But if we increase it by one more, Number 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, one cannot say whether it is accurate or not. Any calculations with integers outside the safe integer range (that is, from Number.MIN_SAFE_INTEGER to Number.MAX_SAFE_INTEGER ) are potentially not accurate. For this reason, we can only rely on integer values ​​in the safe range.


New: BigInt


BigInt is a new numeric primitive data type in JavaScript that allows you to work with arbitrary precision numbers. With BigInt you can safely store and process large integers even beyond the maximum safe integer value Number .


To create BigInt suffices to add the suffix n to the literal notation of an integer. For example, 123 will become 123n . The global function BigInt(number) can be used to cast a number to BigInt . In other words, BigInt(123) === 123n . Let's use this to solve the problems 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 of type 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 must 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 safe operation with integers do not apply to BigInt , so with 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 can return a typeof operator:


 typeof 123; // β†’ 'number' typeof 123n; // β†’ 'bigint' 

Since BigInt is an independent data type, a number of type BigInt never be strictly equal to a number of type (for example, 42n !== 42 ). To compare a number of type BigInt and a number of type Number , convert one of them to the type of the other before making a comparison, or use a comparison with type conversion ( == ):


 42n === BigInt(42); // β†’ true 42n == 42; // β†’ true 

When casting to a boolean value (for example, in if , using && or || , or as the result of a Boolean(int) expression, and so on), numbers of type BigInt behave exactly like numbers of type.


 if (0n) { console.log('if'); } else { console.log('else'); } // β†’ logs 'else', because `0n` is falsy. 

Operators


BigInt supports most operators. Binary + , - , * and ** work as usual. / and % also work, rounding the result to a whole if necessary. Bitwise Operators | , & , << , >> and ^ work with BigInt numbers in the same way as Number numbers, when negative numbers are represented in binary form as an additional code.


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

Unary - can be used to indicate a negative BigInt value, for example, -42n . Unary + not supported because it violates the asm.js code, which expects that +x will always return either a Number or an exception.


The important point is that you should not mix BigInt and Number in operations. 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 be equal to the result? There is no right answer. BigInt cannot contain fractional numbers, and Number cannot accurately contain large numbers greater than the safe integer limit. Therefore, operations with BigInt and Number result in a TypeError exception.


The only exception to this rule is comparison operators, such as === (discussed earlier), < and >= , since they return logical values ​​that do not carry the risk of losing accuracy.


 1 + 1n; // β†’ TypeError 123 < 124n; // β†’ true 

Note: since BigInt and Number usually do not mix, it is not necessary to overwrite the already existing code from Number to BigInt . Decide which of these two types you need and use it. For new APIs that work with potentially large integers, BigInt is the best choice. However, Number can still be used for values ​​that are guaranteed to be in the safe range of integers.


It is also worth noting that the >>> operator, which performs an unsigned right shift, does not make sense for BigInt numbers, since they always contain a sign. Therefore, >>> does not work for BigInt numbers.


API


Several new API methods for BigInt become available.


The BigInt global constructor BigInt similar to the Number constructor: it converts its argument to BigInt (as mentioned earlier). If the conversion fails, a SyntaxError or RangeError exception will be thrown.


 BigInt(123); // β†’ 123n BigInt(1.5); // β†’ RangeError BigInt('1.5'); // β†’ SyntaxError 

There are two functions that allow you to limit BigInt values BigInt specified number of significant bits, treating the number as either signed or unsigned. BigInt.asIntN(width, value) limit the number value type BigInt to the number of bits specified in width taking into account the sign, and BigInt.asUintN(width, value) will do 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 // ^  ,     

Notice that the overflow occurs as soon as we pass a BigInt that exceeds the 64-bit integer range (i.e. 63 bits for the value itself and 1 bit for the character).


BigInt allows BigInt 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 working with these values:


 const view = new BigInt64Array(4); // β†’ [0n, 0n, 0n, 0n] view.length; // β†’ 4 view[0]; // β†’ 0n view[0] = 42n; view[0]; // β†’ 42n 

BigInt64Array guarantees that its values ​​will be within the limits 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 similarly for unsigned 64-bit values.


BigInt fun with BigInt !


')

Source: https://habr.com/ru/post/354930/


All Articles