Float64における整数精度の限界

JavaScriptのNumber.MAX_SAFE_INTEGERが2^53-1である理由と、それを超えた場合に何が起こるかを学びます。仮数ビットと整数精度の関係を理解しましょう。

Precision

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の倍数のみが正確です。

実用的な影響:

  1. データベースID: 2^53を超えるデータベースのID(TwitterのSnowflake IDなど)は、JavaScript数値としてパースされると精度を失います。APIが大きなIDを文字列として返すのはこのためです。

  2. 金融計算: 0.01(1セント)はfloat64で正確に表現できません。セントを整数として扱うことでこれを回避できますが、整数がMAX_SAFE_INTEGERを超えない場合に限ります。

  3. ビット演算: JavaScriptのビット演算子(|&^~<<>>)は32ビット整数で動作し、上位ビットを切り捨てます。(2**53) | 00を返します。

解決策:

  • BigInt(JavaScript): 9007199254740993n + 1nは正しく動作
  • Long(Java): 2^63 - 1までの範囲の64ビット整数
  • 10進数ライブラリ: 正確な10進算術用
  • 文字列表現: JSONで大きなIDを文字列として渡す

ユースケース

整数精度の限界の理解は、大きな数値IDを返すAPIを扱うWeb開発者、通貨値の算術を行う金融アプリケーション、および異なる整数・浮動小数点表現を持つ言語間でコードを移植する人にとって重要です。

試してみる — IEEE 754 Inspector

フルツールを開く