Float64における整数精度の限界
JavaScriptのNumber.MAX_SAFE_INTEGERが2^53-1である理由と、それを超えた場合に何が起こるかを学びます。仮数ビットと整数精度の関係を理解しましょう。
Decimal Value
9007199254740992
Float32 Hex
0x5A000000
Float64 Hex
0x4340000000000000
詳細な説明
JavaScriptなど、float64を主要な数値型として使用する言語は、特定の限界までしか整数を正確に表現できません。その限界を超えると、連続する整数を区別できなくなります。
安全な整数範囲:
Number.MAX_SAFE_INTEGER = 2^53 - 1 = 9007199254740991
Number.MIN_SAFE_INTEGER = -(2^53 - 1) = -9007199254740991
なぜ2^53か?
Float64は52ビットの仮数と1つの暗黙の先頭ビットを持ち、53ビットの有効数字精度を提供します。53ビットに収まる整数は正確に格納できます。54ビット以上を必要とする整数は丸められる必要があります。
境界で何が起こるか:
9007199254740991 + 1 // 9007199254740992 ✓(正確、これは2^53)
9007199254740992 + 1 // 9007199254740992 ✗(...993のはずが...992に丸められる)
9007199254740992 + 2 // 9007199254740994 ✓(この大きさでは偶数が正確)
9007199254740993 === 9007199254740992 // true!(区別できない)
2^53では、ULP(表現可能な値のギャップ)は2になります。偶数の整数のみが表現可能です。2^54ではギャップは4になり、4の倍数のみが正確です。
実用的な影響:
データベースID: 2^53を超えるデータベースのID(TwitterのSnowflake IDなど)は、JavaScript数値としてパースされると精度を失います。APIが大きなIDを文字列として返すのはこのためです。
金融計算:
0.01(1セント)はfloat64で正確に表現できません。セントを整数として扱うことでこれを回避できますが、整数がMAX_SAFE_INTEGERを超えない場合に限ります。ビット演算: JavaScriptのビット演算子(
|、&、^、~、<<、>>)は32ビット整数で動作し、上位ビットを切り捨てます。(2**53) | 0は0を返します。
解決策:
- BigInt(JavaScript):
9007199254740993n + 1nは正しく動作 - Long(Java): 2^63 - 1までの範囲の64ビット整数
- 10進数ライブラリ: 正確な10進算術用
- 文字列表現: JSONで大きなIDを文字列として渡す
ユースケース
整数精度の限界の理解は、大きな数値IDを返すAPIを扱うWeb開発者、通貨値の算術を行う金融アプリケーション、および異なる整数・浮動小数点表現を持つ言語間でコードを移植する人にとって重要です。