[JavaScript]整数の最大値

2015年7月13日月曜日

JavaScript

JavaScriptで整数をインクリメントしていくと、あるところでそれ以上大きくならなくなる。

for (let i = 0; i < veryLargeNumber; i++) {
     ...
}
...
9007199254740989
9007199254740990
9007199254740991
9007199254740992
9007199254740992
9007199254740992
9007199254740992
...


9007199254740992をインクリメントしても9007199254740992になってしまう。
JavaScriptのNumber型は64bitの倍精度浮動小数点型で表現されるので、整数を正確に表せるのは仮数部の53bit分ということになる。

16進数で書くとこんな。
0x1FFFFFFFFFFFFF
9007199254740991(10進)

だから、9007199254740991を超えると整数の値が不正確なものになってしまう。
試しに以下を実行して、10進表現と2進表現を表示すると

for (let i = 0; i < 10; i++) {
    print(0x1FFFFFFFFFFFFF + i + ': ' + (0x1FFFFFFFFFFFFF + i).toString(2));
}


9007199254740991: 11111111111111111111111111111111111111111111111111111
9007199254740992: 100000000000000000000000000000000000000000000000000000
9007199254740992: 100000000000000000000000000000000000000000000000000000
9007199254740994: 100000000000000000000000000000000000000000000000000010
9007199254740996: 100000000000000000000000000000000000000000000000000100
9007199254740996: 100000000000000000000000000000000000000000000000000100
9007199254740996: 100000000000000000000000000000000000000000000000000100
9007199254740998: 100000000000000000000000000000000000000000000000000110
9007199254741000: 100000000000000000000000000000000000000000000000001000
9007199254741000: 100000000000000000000000000000000000000000000000001000



9007199254740992が2回現れたり、9007199254740996が3回現れたりするけど、9007199254740993や9007199254740995というのは現れない。
これは仮数部の端数処理が2進数の再近接偶数丸めで行われているからだ。端数は最も近い表現可能な数に丸められるけど、上下の表現可能な数から等距離の場合、丸め後の値が偶数になるように丸められる。
その結果、以下の矢印の方向にまるめられ、9007199254740992が2回出現し、9007199254740996が3回出現することになる。

9007199254740992: 10000000000000000000000000000000000000000000000000000 0
9007199254740993: 10000000000000000000000000000000000000000000000000000 1↓
9007199254740994: 10000000000000000000000000000000000000000000000000001 0
9007199254740995: 10000000000000000000000000000000000000000000000000001 1↑
9007199254740996: 10000000000000000000000000000000000000000000000000010 0
9007199254740997: 10000000000000000000000000000000000000000000000000010 1↓
9007199254740998: 10000000000000000000000000000000000000000000000000011 0