1. ホーム
  2. python

[解決済み] Python 3 で浮動小数点値 4*0.1 はきれいに見えるのに、3*0.1 はそう見えないのはなぜですか?

2022-04-25 17:04:43

質問

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

しかし、なぜ 4*0.1 としてきれいに印刷されます。 0.4 しかし 3*0.1 は、そうではありません。 どちらの値も、実際には醜い10進数表現をしています。

>>> 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')

解決方法は?

答えは簡単です。 3*0.1 != 0.3 量子化(丸め)誤差のため(一方 4*0.1 == 0.4 なぜなら、2の累乗をかけることは通常 "正確"な操作だからです)。Pythonは を丸めることができる最短の文字列です。 を表示することができます。 4*0.1 として 0.4 を表示することはできません。 3*0.1 として 0.3 というのも、これらは等しくないからです。

を使用することができます。 .hex メソッドを使用すると、数値の内部表現を見ることができます(基本的に、Pythonの 正確 バイナリ浮動小数点値で、基数10の近似値ではありません)。これは、ボンネットの下で何が起こっているかを説明するのに役立ちます。

>>> (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.9999999999aの2^-4倍です。末尾の "a" は 10 の桁を意味し、言い換えれば、2 進数の浮動小数点数での 0.1 は次のようになります。 ごくわずかに 最終的に0x0.99が切り上げられ0x0.aになるので)0.1というquot;exact"値より大きくなります。これを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 の float repr が設計されています。 ラウンドトリッパブル つまり、表示された値は元の値に正確に変換できる必要があります ( float(repr(f)) == f すべてのフロートについて f ). そのため 0.30.1*3 と全く同じように、あるいは2つの 異なる の数値は、往復で同じになってしまいます。その結果、Python 3 の repr エンジンは、わずかに見かけ上の誤差があるものを表示するように選択します。