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,991or 2**53 – 1or less than -9,007,199,254,740,991or . -(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. BigIntThe introduction of JavaScript brings the total number of JavaScript data types to eight. In this article, we'll learn BigIntwhat 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?

BigIntis 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 BigIntvalue must contain a lowercase letter after the number n, 897ne.g. Therefore, it is accurate to say that is 21not strictly equal to 21n, since the former is one numberand the latter is one bigint.

When you perform math on any number greater than 9,007,199,254,740,991or 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 BigIntto 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 non 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 BigIntof type we can use typeofthe 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 : BigIntPassing numbers in constructors is not recommended. Instead, it's better to just append nor wrap it in a string first, as in sampleBigIntOneand respectively sampleBigIntThree.

BigInt usage examples

BigIntValues ​​are used in areas of life or computing where large numbers are of paramount importance. Here are BigIntsome key use cases for :

  • Financial Calculations: BigIntImportant in financial calculations as very large volumes of transactions and currency conversions can be handled
  • Cryptography and Secure Computing: BigIntUsed 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. BigIntThe use of ensures accurate representation of such values
  • Distributed Systems: Distributed systems require unique identities to perform accurately. Since BigIntvalues ​​are infinite, they can be used to generate identifiers and keys

BigIntIt'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 69and 4.125677. It includes values ​​between 9,007,199,254,740,991or 2**53 – 1and -9,007,199,254,740,991or . These limits are available as constants, such as and -(2**53 – 1)respectively .Number.MAX_SAFE_INTEGERNumber.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 BigIntnot Absolutely.

BigIntCan 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,991or less than . -9,007,199,254,740,991Such 17.2ndecimal values ​​are invalid.

In general, all arithmetic operations except ( ) give the correct mathematical answer BigIntwhen 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/2n2n2.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_VALUEandMIN_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/10and 9,007,199,254,740,991or 2**53 – 1to -9,007,199,254,740,991or -(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,991or 2**53 – 1to -9,007,199,254,740,991or . -(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_VALUEand Number.MIN_VALUE. They are essentially numbers: 1.7976931348623157e+308and , respectively 5e-324.

Whenever you try to Number.MAX_VALUEperform addition or subtraction using , it returns 1.7976931348623157e+308. When you try to Number.MIN_VALUEdo 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_VALUEperform 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, BigIntwith 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, BigIntmake 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

BigIntArbitrary 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_INTEGERBigInt

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_VALUEperform 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 BigIntdon'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, BigIntit 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 BigIntprovided on the prototype .asIntNasUintN

Essentially, these two methods are used to wrap BigIntvalues ​​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 asIntNthe method BigInt.asIntN(width, value)to do so by calling it. In this case, widththe argument represents the required bits greater than zero, and valuethe argument represents the value we want to limit within the provided widthvalue 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 asIntNcreated the 16-bit integer limit to be used by using . Generally speaking, 16 bits can only contain integers between to -32768n. 32767nSo, 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.

BigIntAlso 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, BigIntthe advent of JavaScript can improve applications that perform heavy calculations.

How to use BigInt in JavaScript

Because BigIntis a data type, it Numbercan also be used to perform calculations, similar to . To define BigInta value, we can use BigIntthe identifier (i.e. n), or use BigIntthe 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 BigIntfive 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 BigIntthe object instance (such as 5n.toString()), while a BigInt.asUintN()method like will BigIntbe called directly on the constructor BigInt.asIntN(maximumBits, valueTwo).

BigInt.prototype.toString()

First, let's take a look BigInt.prototype.toString(). toString()Used to BigIntconvert a value to a string. It essentially wraps it in double or single quotes BigIntwhile removing the trailing ones n. It can be used like this:

const value1 = 35n;
const sample1 = value1.toString();

console.log(sample1); // "35"

You can typeofverify 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 BigIntpass 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()

valueOfMethod used to obtain BigIntthe 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 localeand 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 BigIntlimit a value to a set bit width while preserving the sign of the value. Syntax: BigInt.asIntN(bitWidth, BigIntValue). BigIntTherefore, it is suitable when we want to preserve the sign of a value (even if we want to constrain it).

Generally speaking, bitWidthit 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 -128to 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(). BigIntThe 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 Numberalso apply to BigInt. This means that ||, &&and !can also BigIntoperate normally with . Also, when we use ifthe statement, only the value 0nis 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 Booleanthe function works as expected, only 0nwill 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 BigIntwithout errors we need to follow some rules:

  • BigIntDo not use newthe keyword when creating
  • Don't Bigintuse decimals in
  • BigIntcast error
  • Do not use directly JSON.stringifywithBigInt
  • Limited built-in methods
  • BigIntThe division operation returns a truncated result

First, BigIntdon't use newthe keyword when creating . In JavaScript, we often use newthe keyword to initialize instances of a class. However, BigIntthis is not the case because when we use newthe keyword, it throws TypeError:

const value = new BigInt(54);
// TypeError: BigInt is not a constructor

Also, don't Bigintuse decimals in . BigIntIt 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 BigIntcast errors, for example mixing Numberwith BigIntwill throw TypeError:

const result = 2n + 4;
console.log(result);
// TypeError: Cannot mix BigInt and other types, use explicit conversions

Also, using null, undefinedand Symbolwith BigIntwill 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.stringifywith BigInt. We can't BigIntuse it directly on JSON.stringifywill throw TypeError:

const theStringified = JSON.stringify(5n);
console.log(theStringified);
// TypeError: BigInt value can't be serialized in JSON

Instead, we can BigIntstringify the value to implement toJSONthe method or implement a replacermethod. To implement toJSONthe 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 replacerimplement 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

BigIntOne 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 TypeErroran exception:

console.log(Math.sqrt(4n))
// TypeError: can't convert BigInt to number

Additionally, BigIntdivision using returns a truncated result. Because BigIntvalues ​​cannot be decimals, division operations sometimes return decimal dividends, for example 7 / 4 = 1.75. Therefore, BigIntthe value will always be rounded to 0. This means 7n / 4n = 1nrather than what is expected 1.75n.

Browser and Node.js support

v10.4Any Node.js version since (including newer v18.16versions) 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 BigIntconstructors and their methods.

summary

In this article, we learned about BigIntits available methods, use cases, and issues with using it. However, we should know that it Numberis 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.

Guess you like

Origin blog.csdn.net/p1967914901/article/details/131387492