下記のような記事を見つけたので「動くな!浮動小数点数警察だ!」と叫んで乗り込んでみます。
とはいえ、そんなに大きなツッコミどころは無いとも思っています。
多くの人の感想の通り、浮動小数点数一般の話題であってPythonに特化した話題ではないとか、巨大だから変といわれると少しもにょる(「2^53を超える巨大な整数を浮動小数点数演算したら変」なら一発で内容がわかる)とか、言い回しがおかしいところはあるんですが、大きく誤解している部分は無いんじゃないかなと思いました。
ただ、一点だけ下記のような記述がありまして、
109999999999999995805696 0x1.74b1ca8ab05a8p+76 〜 この間はすべて 0x1.74b1ca8ab05a9p+76 〜 110000000000000012582912 0x1.74b1ca8ab05aap+76
その区間の幅は、2の24乗 で 16777216
このあたりが若干気になりました。この区間の幅を2^24と言っていいんでしょうか?詳しく見てみましょう。
.hex() での浮動小数点数表現 |
同じ表現になる最小の整数 | 同じ表現になる最大の整数 | この区間に含まれる整数の個数 |
---|---|---|---|
0x1.74b1ca8ab05a8p+76 | 109999999999999979028480 | 109999999999999995805696 | 16777217 |
0x1.74b1ca8ab05a9p+76 | 109999999999999995805697 | 110000000000000012582911 | 16777215 |
0x1.74b1ca8ab05aap+76 | 110000000000000012582912 | 110000000000000029360128 | 16777217 |
このように、同じ浮動小数点数表現になる区間の幅を考えると一定ではないことがわかります。だいたい2^24だからいいんだよ!という理解でもいいんですけど、せっかくここまで調べたんだからもう少しだけ深掘りしてもいいんじゃないでしょうか。
偶数丸め
この一見不思議な現象の犯人は浮動小数点数の丸め方式である「偶数丸め」です。これは銀行家丸めなどと呼ばれることもあるのですが、ざっくり言うと「表現できる数のど真ん中だったら偶数に丸める、それ以外は近い方に丸める」というものです。
要するに、0.5を整数に丸めるなら1ではなく0に丸める、1.5を丸めるなら2に丸める、ということになります。
いまは整数しか扱っていないのに「偶数に丸める」と言われても意味がわからないかもしれませんが、浮動小数点数の言葉で言い換えると「表現できる数のど真ん中だったら仮数部の最下位ビットが0である方に丸める」ということです。
109999999999999995805696を浮動小数点数に丸める場合、最近接の浮動小数点数は109999999999999987417088.0 (=0x1.74b1ca8ab05a8p+76)と110000000000000004194304.0 (=0x1.74b1ca8ab05a9p+76)で、どちらも同じ距離になります。この場合、前者が偶数なので、前者に丸められます。
つまり、偶数丸めでは偶数に丸められる整数が本来の区間の幅(?)より1個多くなり、奇数に丸められる整数は1個少なくなるので、上のように区間の幅が交互に増えたり減ったりするというわけです。
蛇足
多倍長整数警察は他の人にお任せします
浮動小数点数の16進表記は仮数部の最下位ビットが0か1か一目でわかって素晴らしいですね。もっと多くの言語で採用して欲しいものです。