JavaScriptの最新データ型の基本ガイド:BigInt

公開: 2022-03-10
簡単な要約↬JavaScriptでは、 Numberタイプは253より大きい整数値を安全に表すことができません。 この制限により、開発者は非効率的な回避策とサードパーティライブラリを使用する必要がありました。 BigIntは、それを修正することを目的とした新しいデータ型です。

BigIntデータ型は、JavaScriptプログラマーがNumberデータ型でサポートされている範囲よりも大きい整数値を表現できるようにすることを目的としています。 大きな整数に対して数学演算を実行する場合、任意の精度で整数を表す機能は特に重要です。 BigIntを使用すると、整数のオーバーフローは問題になりません。

さらに、回避策を使用せずに、高解像度のタイムスタンプや大きな整数IDなどを安全に操作できます。 BigIntは現在、ステージ3の提案です。 仕様に追加されると、JavaScriptの2番目の数値データ型になります。これにより、サポートされるデータ型の総数は8つになります。

  • ブール値
  • ヌル
  • 未定義
  • 番号
  • BigInt
  • ストリング
  • シンボル
  • 物体

この記事では、 BigIntをよく見て、JavaScriptのNumberタイプの制限を克服するのにどのように役立つかを見ていきます。

問題

JavaScriptに明示的な整数型がないことは、他の言語を使用するプログラマーにとっては困惑することがよくあります。 多くのプログラミング言語は、float、double、integer、bignumなどの複数の数値型をサポートしていますが、JavaScriptの場合はそうではありません。 JavaScriptでは、すべての数値は、IEEE754-2008標準で定義されている倍精度64ビット浮動小数点形式で表されます。

ジャンプした後もっと! 以下を読み続けてください↓

この標準では、正確に表現できない非常に大きな整数は自動的に丸められます。 正確には、JavaScriptのNumberタイプは、-9007199254740991(-(2 53 -1))から9007199254740991(2 53 -1)までの整数のみを安全に表すことができます。 この範囲外の整数値は、精度を失う可能性があります。

これは、次のコードを実行することで簡単に調べることができます。

 console.log(9999999999999999); // → 10000000000000000

この整数は、JavaScriptがNumberプリミティブで確実に表すことができる最大数よりも大きくなります。 したがって、丸められます。 予期しない丸めは、プログラムの信頼性とセキュリティを損なう可能性があります。 別の例を次に示します。

 // notice the last digits 9007199254740992 === 9007199254740993; // → true

JavaScriptは、JavaScriptで最大の安全な整数をすばやく取得できるようにするNumber.MAX_SAFE_INTEGER定数を提供します。 同様に、 Number.MIN_SAFE_INTEGER定数を使用して、安全な最小整数を取得できます。

 const minInt = Number.MIN_SAFE_INTEGER; console.log(minInt); // → -9007199254740991 console.log(minInt - 5); // → -9007199254740996 // notice how this outputs the same value as above console.log(minInt - 4); // → -9007199254740996

ソリューション

これらの制限の回避策として、一部のJavaScript開発者は、 String型を使用して大きな整数を表します。 たとえば、Twitter APIは、JSONで応答するときに、文字列バージョンのIDをオブジェクトに追加します。 さらに、大きな整数の操作を容易にするために、bignumber.jsなどの多くのライブラリが開発されています。

BigIntを使用すると、アプリケーションは、 Number.MAX_SAFE_INTEGERおよびNumber.Min_SAFE_INTEGERを超える整数を安全に表すための回避策やライブラリを必要としなくなります。 大きな整数の算術演算は、精度を損なうことなく、標準のJavaScriptで実行できるようになりました。 サードパーティのライブラリよりもネイティブデータ型を使用することの追加の利点は、実行時のパフォーマンスが向上することです。

BigIntを作成するには、整数の末尾にnを追加するだけです。 比較:

 console.log(9007199254740995n); // → 9007199254740995n console.log(9007199254740995); // → 9007199254740996

または、 BigInt()コンストラクターを呼び出すこともできます。

 BigInt("9007199254740995"); // → 9007199254740995n

BigIntリテラルは、2進数、8進数、または16進数の表記で記述することもできます。

 // binary console.log(0b100000000000000000000000000000000000000000000000000011n); // → 9007199254740995n // hex console.log(0x20000000000003n); // → 9007199254740995n // octal console.log(0o400000000000000003n); // → 9007199254740995n // note that legacy octal syntax is not supported console.log(0400000000000000003n); // → SyntaxError

BigIntは同じタイプではないため、厳密な等式演算子を使用してBigIntを通常の数値と比較することはできないことに注意してください。

 console.log(10n === 10); // → false console.log(typeof 10n); // → bigint console.log(typeof 10); // → number

代わりに、オペランドを比較する前に暗黙の型変換を実行する等式演算子を使用できます。

 console.log(10n == 10); // → true

単項プラス( + )演算子を除いて、すべての算術演算子をBigIntで使用できます。

 10n + 20n; // → 30n 10n - 20n; // → -10n +10n; // → TypeError: Cannot convert a BigInt value to a number -10n; // → -10n 10n * 20n; // → 200n 20n / 10n; // → 2n 23n % 10n; // → 3n 10n ** 3n; // → 1000n let x = 10n; ++x; // → 11n --x; // → 10n

単項プラス( + )演算子がサポートされていない理由は、一部のプログラムが、 +が常にNumberを生成するか、例外をスローするという不変条件に依存している可能性があるためです。 +の動作を変更すると、asm.jsコードも破損します。

当然、 BigIntオペランドとともに使用すると、算術演算子はBigInt値を返すことが期待されます。 したがって、除算( / )演算子の結果は自動的に切り捨てられます。 例えば:

 25 / 10; // → 2.5 25n / 10n; // → 2n

暗黙の型変換

暗黙的な型変換は情報を失う可能性があるため、 BigIntNumberの混合演算は許可されていません。 大きな整数と浮動小数点数を混在させる場合、結果の値はBigIntまたはNumberで正確に表現できない場合があります。 次の例を考えてみましょう。

 (9007199254740992n + 1n) + 0.5

この式の結果は、 BigIntNumberの両方のドメインの外にあります。 小数部分のあるNumberは、 BigIntに正確に変換できません。 また、2 53より大きいBigIntは、 Numberに正確に変換できません。

この制限の結果として、 NumberオペランドとBigIntオペランドを組み合わせて算術演算を実行することはできません。 また、 BigIntをWebAPIおよびNumberを期待する組み込みのJavaScript関数に渡すこともできません。 そうしようとすると、 TypeErrorが発生します:

 10 + 10n; // → TypeError Math.max(2n, 4n, 6n); // → TypeError

この例に示すように、関係演算子はこの規則に従わないことに注意してください。

 10n > 5; // → true

BigIntNumberを使用して算術計算を実行する場合は、最初に、操作を実行するドメインを決定する必要があります。 これを行うには、 Number()またはBigInt()を呼び出して、いずれかのオペランドを変換するだけです。

 BigInt(10) + 10n; // → 20n // or 10 + Number(10n); // → 20

Booleanコンテキストで検出された場合、 BigIntNumberと同様に扱われます。 言い換えると、 BigIntは、 0nでない限り、真の値と見なされます。

 if (5n) { // this code block will be executed } if (0n) { // but this code block won't }

配列をソートするときに、 BigInt型とNumber型の間の暗黙的な型変換は発生しません。

 const arr = [3n, 4, 2, 1n, 0, -1n]; arr.sort(); // → [-1n, 0, 1n, 2, 3n, 4]

|などのビット演算子、 & ​​、 <<>> 、および^は、 Numberと同様にBigIntを操作します。 負の数は、無限長の2の補数として解釈されます。 混合オペランドは許可されていません。 ここではいくつかの例を示します。

 90 | 115; // → 123 90n | 115n; // → 123n 90n | 115; // → TypeError

BigIntコンストラクタ

他のプリミティブ型と同様に、 BigIntはコンストラクター関数を使用して作成できます。 BigInt()に渡された引数は、可能であれば自動的にBigIntに変換されます。

 BigInt("10"); // → 10n BigInt(10); // → 10n BigInt(true); // → 1n

変換できないデータ型と値は例外をスローします。

 BigInt(10.2); // → RangeError BigInt(null); // → TypeError BigInt("abc"); // → SyntaxError

コンストラクターを使用して作成されたBigIntに対して、算術演算を直接実行できます。

 BigInt(10) * 10n; // → 100n

厳密な等式演算子のオペランドとして使用される場合、コンストラクターを使用して作成されたBigIntは、通常のものと同様に扱われます。

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

ライブラリ関数

JavaScriptは、 BigInt値を符号付きまたは符号なし整数として表すための2つのライブラリ関数を提供します。

  • BigInt.asUintN(width, BigInt)BigIntを0と2の-1の間でラップします
  • BigInt.asIntN(width, BigInt) :BigIntをBigInt - 1と2width-1-1の間でラップします

これらの関数は、64ビットの算術演算を実行するときに特に役立ちます。 このようにして、意図した範囲内にとどまることができます。

ブラウザのサポートとトランスパイル

この記事の執筆時点では、Chrome + 67とOpera + 54はBigIntデータ型を完全にサポートしています。 残念ながら、EdgeとSafariはまだ実装していません。 FirefoxはデフォルトでBigIntをサポートしていませんが、 about:configjavascript.options.biginttrueに設定することで有効にできます。 サポートされているブラウザの最新リストは、Can Iuse…で入手できます。

残念ながら、 BigIntのトランスパイルは非常に複雑なプロセスであり、実行時のパフォーマンスが大幅に低下します。 また、提案によって既存の複数の演算子の動作が変更されるため、 BigIntを直接ポリフィルすることもできません。 今のところ、より良い代替手段は、 BigInt提案の純粋なJavaScript実装であるJSBIライブラリを使用することです。

このライブラリは、ネイティブのBigIntとまったく同じように動作するAPIを提供します。 JSBIの使用方法は次のとおりです。

 import JSBI from './jsbi.mjs'; const b1 = JSBI.BigInt(Number.MAX_SAFE_INTEGER); const b2 = JSBI.BigInt('10'); const result = JSBI.add(b1, b2); console.log(String(result)); // → '9007199254741001'

JSBIを使用する利点は、ブラウザーのサポートが改善されたら、コードを書き直す必要がないことです。 代わりに、babelプラグインを使用して、JSBIコードをネイティブBigIntコードに自動的にコンパイルできます。 さらに、JSBIのパフォーマンスは、ネイティブのBigInt実装と同等です。 間もなくBigIntの幅広いブラウザサポートが期待できます。

結論

BigIntは、整数値がNumberデータ型でサポートされている範囲よりも大きい場合に使用することを目的とした新しいデータ型です。 このデータ型を使用すると、ライブラリを使用しなくても、大きな整数に対して安全に算術演算を実行したり、高解像度のタイムスタンプを表したり、大きな整数IDを使用したりできます。

NumberオペランドとBigIntオペランドを組み合わせて算術演算を実行することはできないことに注意してください。 いずれかのオペランドを明示的に変換して、操作を実行するドメインを決定する必要があります。 さらに、互換性の理由から、 BigIntで単項プラス( + )演算子を使用することは許可されていません。

どう思いますか? BigIntは便利だと思いますか? コメントで教えてください!