JavaScript/長整数
長整数(長整数、Big integer)は多倍長整数です。 長整数は数型の範囲を超える整数を精度を失うことなく扱う事ができます。 BigIntがプリミティブ長整数に対応するラッパーオブジェクトオブジェクトです。
概要
編集JavaScriptでは従来から数値を扱うオブジェクト Number がサポートされています。 Numberは IEEE754の倍精度64ビット浮動小数点数です。 このため整数は倍精度64ビット浮動小数点数の仮数部の精度しかなく、さらにビット操作はそのうち32bitだけが使われます。
この様な事情から、任意長演算による整数の実装のニーズが有りました。
長整数リテラル
編集長整数リテラルは整数リテラルの末尾に 'n' 1文字を伴った書式をしています。
42n
-1n
0n
864e5n
0xFFFFn
0b1101n
0o3162n
1_234_567n
2_3_5_7_11n
単項+はNumberへの暗黙の型変換を引き起こすため
+1024n
TypeError: Cannot convert a BigInt value to a number となります
型と型変換
編集長整数に typeof 演算子を適用すると "bigint" を返します。
typeof 999999n; // "bigint"
BigInt()はコンストラクタではないので new BigInt() は TypeError になります。 BigInt("123") の様に new を付けずに呼び出します。
typeof BigInt("123"); // "bigint"
演算子
編集この節は書きかけです。この節を編集してくれる方を心からお待ちしています。
算術演算子
編集長整数では数値と同じく +, *, -, **と% の算術演算子が使えます。
長整数と数値が混在する算術演算は TypeError となります。
1 + 1n; // TypeError:
2n + 8; // TypeError:
長整数の算術演算ではゼロ除算は RangeError となります。
1n / 0n; // RangeError:
9n % 0n; // RangeError:
0n / 0n; // RangeError:
try / catch で捕捉しない限りプログラムはゼロ除算が出た時点で止まります。
ビット操作演算子
編集長整数では数値と同じくビット操作演算子が使えます。 長整数と数値が混在するビット操作演算は TypeError となります。 >>> (論理的右シフト) は長整数にはありません。BigInts have no unsigned right shift, use >> instead
比較演算子
編集算術演算子やビット操作演算子と異なり、長整数と数値の間の比較演算子は型が違っても行えます。
1n == 0; // false
1n == 1; // true
10n != "10"; // false
厳密比較演算子は両辺の型が一致しない場合は不一致となります。
1n === 0; // false
1n === 1; // false
10n !== "10"; // true
使用例
編集多倍長整数演算の特徴を生かした短いプログラムを紹介します。
円周率を77桁求める
let k = 2n, a = 4n, b = 1n, a1 = 12n, b1 = 4n;
let result = "";
[k, a, b, a1, b1] = [2n, 4n, 1n, 12n, 4n];
for (let i = 0; i < 100; i++) {
let p = k * k, q = 2n * k + 1n;
k += 1n;
[a, b, a1, b1] = [a1, b1, p * a + q * a1, p * b + q * b1];
d = a / b;
d1 = a1 / b1;
while (d == d1) {
result += Number(d).toString();
[a, a1] = [10n * (a % b), 10n * (a1 % b1)];
[d, d1] = [a / b, a1 / b1];
}
}
console.log(result.length); // 77
console.log(result); // 31415926535897932384626433832795028841971693993751058209749445923078164062862
console.log(Math.PI.toFixed(77)); // 3.14159265358979311599796346854418516159057617187500000000000000000000000000000
徒に行数が長くなるので、分割代入を使って行数を短縮しました。 整数リテラルはループカウンタ以外は全て 12n の様な BigIntリテラルにしなければいけませんが、忘れると TypeError になるのでバグ化は意外としにくいです。 Math.PIと一致していませんが、BigIntで求めた値が正確です。