Python 3 では浮動小数点値 4*0.1 はきれいに表示されるのに、3*0.1 は表示されないのはなぜですか? 質問する

Python 3 では浮動小数点値 4*0.1 はきれいに表示されるのに、3*0.1 は表示されないのはなぜですか? 質問する

ほとんどの小数点には正確な浮動小数点表現がないことは知っています(浮動小数点演算は壊れていますか?)。

4*0.1しかし、両方の値が実際には醜い 10 進表現であるのに、は としてきれいに印刷されるのに0.4、は印刷3*0.1されないのはなぜかわかりません。

>>> 3*0.1
0.30000000000000004
>>> 4*0.1
0.4
>>> from decimal import Decimal
>>> Decimal(3*0.1)
Decimal('0.3000000000000000444089209850062616169452667236328125')
>>> Decimal(4*0.1)
Decimal('0.40000000000000002220446049250313080847263336181640625')

ベストアンサー1

簡単な答えは、3*0.1 != 0.3量子化(丸め)誤差によるものです(4*0.1 == 0.42の累乗は通常「正確な」演算であるため)。Pythonは目的の値に丸められる最短の文字列4*0.1これらは等しいのでと表示できますが、これらは等しくないのでと0.4表示できません。3*0.10.3

Pythonのメソッドを使用して.hex、数値の内部表現(基本的にはちょうど10 進近似値ではなく、2 進浮動小数点値を使用します。これにより、内部で何が起こっているかが説明できます。

>>> (0.1).hex()
'0x1.999999999999ap-4'
>>> (0.3).hex()
'0x1.3333333333333p-2'
>>> (0.1*3).hex()
'0x1.3333333333334p-2'
>>> (0.4).hex()
'0x1.999999999999ap-2'
>>> (0.1*4).hex()
'0x1.999999999999ap-2'

0.1は0x1.999999999999aの2^-4倍です。末尾の「a」は数字の10を意味します。言い換えると、2進浮動小数点数の0.1はほんの少し「正確な」値 0.1 よりも大きくなります (最後の 0x0.99 は 0x0.a に切り上げられるため)。これを 2 の累乗である 4 で乗算すると、指数は上方にシフトします (2^-4 から 2^-2) が、それ以外は数値は変化しません4*0.1 == 0.4

しかし、3倍にすると、0x0.99と0x0.a0(0x0.07)のわずかな差が0x0.15の誤差に拡大し、最後の桁に1桁の誤差として現れます。これにより、0.1*3はほんの少し丸められた値 0.3 より大きい。

Python 3のfloatreprは、往復可能つまり、表示される値は元の値に正確に変換可能でなければなりません(float(repr(f)) == fすべての浮動小数点数の場合)。したがって、とをまったく同じようにf表示することはできません。0.30.1*3違うラウンドトリップ後、数値は同じになります。その結果、Python 3 のreprエンジンは、わずかにエラーがあるものの 1 つを表示することを選択します。

おすすめ記事