フロントエンドの現場で、「なぜか計算結果が微妙にズレる」「バックエンドから来た巨大なIDを扱ったら末尾が0になった」といった悪夢に遭遇したことはないだろうか?
JavaScriptの`Number`型は、IEEE 754という浮動小数点数規格を採用している。これは「実用的な計算を高速に行う」には最適だが、2^53 – 1(`Number.MAX_SAFE_INTEGER`)を超えるような巨大な整数を扱うには、あまりに繊細すぎる。
今日は、そんな限界を突破するために導入された`BigInt`について、現場の知見を交えて深掘りしていく。
—
1. なぜNumberでは巨大な整数を扱えないのか?
まず大前提として、`Number`は「浮動小数点数」であるという事実を忘れてはならない。これは、`1.0000000000000001`のような微細な数値と、巨大な整数を同じ器で扱うためのトレードオフだ。
安全に扱える整数の最大値を超えると、JavaScriptは「精度を切り捨てる」という挙動をとる。
const maxSafe = Number.MAX_SAFE_INTEGER; // 9007199254740991
console.log(maxSafe + 1 === maxSafe + 2); // true(なんと等しい判定になる)
この「誤差」が、金融系のアプリや、IDをJSONでやり取りするシステムでは致命傷になる。そこで登場したのが`BigInt`だ。
2. BigIntの基本と「混ぜるな危険」の理由
`BigInt`は、その名の通り「巨大な整数」を扱うためのプリミティブ型だ。リテラルの末尾に `n` をつけるか、`BigInt()` コンストラクタで生成する。
// BigIntの生成
const hugeNumber = 9007199254740991n;
const anotherHuge = BigInt(“9007199254740992”);
// ここで重要な注意点:暗黙の型変換は発生しない
// NumberとBigIntを混ぜて計算しようとすると、容赦なくTypeErrorが飛んでくる
try {
console.log(hugeNumber + 1); // TypeErrorが発生!
} catch (e) {
console.error(“NumberとBigIntは混ぜるな!”);
}
なぜ言語仕様として厳格に制限しているのか? それは「精度が落ちる可能性のある計算を、開発者の意図しないところで勝手に行わせない」というJavaScript設計陣の強い意志だ。Number(浮動小数点)の世界とBigInt(完全整数)の世界を安易に混ぜると、どちらの仕様を優先すべきか曖昧になる。それを防ぐのがこのエラーの正体だ。
3. 実務で遭遇する「BigIntの罠」と回避策
実務で最も多いトラブルは、「JSONのシリアライズ」だ。`JSON.stringify()` は `BigInt` に対応しておらず、実行すると例外が投げられる。
const data = { id: 12345678901234567890n };
try {
JSON.stringify(data); // TypeError
} catch (e) {
console.log(“BigIntはJSON変換でこけるので注意が必要”);
}
// 解決策:toJSONメソッドを定義して文字列として逃がすのが定石
BigInt.prototype.toJSON = function() {
return this.toString();
};
console.log(JSON.stringify(data)); // ‘{“id”:”12345678901234567890″}’
ブラウザの裏側で何が起きているか?
`BigInt` は、V8エンジン(Chrome/Node.js)等の現代的なエンジンにおいて、メモリ上に可変長の整数として保持される。`Number`がCPUのレジスタ(64bit浮動小数点)に直接乗るのに対し、`BigInt`は必要に応じてメモリを確保するため、演算コストは `Number` よりも若干高い。
高頻度で呼ばれるループの中で `BigInt` を大量生成するのは、メモリとパフォーマンスの観点から避けるべきだ。
4. プロのTips:BigIntを活用する際の手順
実務で `BigInt` を扱うときは、以下のフローを徹底してほしい。
1. 入力時: 外部(APIやDB)から来た巨大な数値は、受け取った瞬間に `BigInt()` でラップする。
2. 計算中: `BigInt` 同士で演算する。もし `Number` と混ぜる必要がある場合は、必ず明示的にキャストする。
- `Number(bigIntVal)` は精度が落ちる可能性がある(要注意)。
3. 出力時: APIに投げる直前に `toString()` して文字列化する。
/
- 現場で使える「型安全な加算関数」の例
/
function safeAdd(a, b) {
// 意図的にBigIntに変換して計算を保証する
return BigInt(a) + BigInt(b);
}
// 使い方の例
const idFromApi = “98765432109876543210”;
const incrementedId = safeAdd(idFromApi, 1n);
console.log(incrementedId.toString()); // “98765432109876543211”
まとめ
`BigInt` は決して「便利な魔法」ではない。むしろ、JavaScriptが抱えていた「巨大数という闇」を、開発者が意識的にコントロールするための「厳格なツール」だ。
「暗黙の型変換がない」という仕様は最初は不便に感じるかもしれない。しかし、大規模なアプリケーションを長期運用する際、この「不便さ」こそが、バグを未然に防ぐ強力な防波堤になる。
もしチームで巨大な数値を扱う機会があれば、まずは `BigInt` を正しく理解し、JSON周りのシリアライズ戦略をチームで共通化しておくこと。これだけで、将来発生しうる「謎の計算ズレ」という悪夢を一つ減らせるはずだ。
次は、`BigInt` と `Math` オブジェクト(`Math.pow` などは `BigInt` を受け付けない)の相性の悪さについて話す必要があるかもしれないね。だが、まずは今日伝えた基本をしっかりと身につけてほしい。現場からは以上だ。

コメント