JavaScriptの最新データ型の基本ガイド:BigInt
公開: 2022-03-10Number
タイプは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
暗黙の型変換
暗黙的な型変換は情報を失う可能性があるため、 BigInt
とNumber
の混合演算は許可されていません。 大きな整数と浮動小数点数を混在させる場合、結果の値はBigInt
またはNumber
で正確に表現できない場合があります。 次の例を考えてみましょう。
(9007199254740992n + 1n) + 0.5
この式の結果は、 BigInt
とNumber
の両方のドメインの外にあります。 小数部分のある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
BigInt
とNumber
を使用して算術計算を実行する場合は、最初に、操作を実行するドメインを決定する必要があります。 これを行うには、 Number()
またはBigInt()
を呼び出して、いずれかのオペランドを変換するだけです。
BigInt(10) + 10n; // → 20n // or 10 + Number(10n); // → 20
Boolean
コンテキストで検出された場合、 BigInt
はNumber
と同様に扱われます。 言い換えると、 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:config
でjavascript.options.bigint
をtrue
に設定することで有効にできます。 サポートされているブラウザの最新リストは、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
は便利だと思いますか? コメントで教えてください!