Edited at

[Python] 浮動小数点数floatの比較は要注意!!


はじめに

最近Pyhonを勉強し始めた新人です。

今回は浮動小数点数floatの比較について書きたいと思います。


float型の比較

皆さん、以下のコードの出力は何だと思いますか?


test.py

def main():

x = 0
for i in range(10):
x += 0.1

if x == 1.0:
print("OK")
else:
print("NG")

if __name__ == "__main__":
main()


0.1を10回足すので、xは1.0になるはずです。

そのため、"OK"が出力されるはず・・・


実際に動かしてみると・・・

実際に動かしてみましょう。


実行

$python test.py

NG

"NG"が表示されます。

数学的には0.1を10回足すと、1.0になるはずですが・・・


xには一体いくつになっているのか?

以下のコードを実行して、型とxの値を確認します。


test.py

def main():

x = 0
for i in range(10):
x += 0.1

print(type(x))
print(x)

if x == 1.0:
print("OK")
else:
print("NG")

if __name__ == "__main__":
main()


実行結果が以下になります。


実行

$ python test.py

<class 'float'>
0.9999999999999999
NG

型はfloat型、そしてxの値は0.9999999999999999となっています。

一体なぜでしょうか?


float型は小数を正しく表示できない!?

実はfloat型は10進数の小数を正しく表示することが出来ません。

以下のコード結果を見れば分かりますが、format()で表示桁数を増やすと誤差が存在するのが分かります。


test.py

print(0.1)

print(format(0.1, '.20f'))


実行

$ python test.py 

0.1
0.10000000000000000555


float型は2進数で管理されている

表示こそ10進数ですが、内部的には2進数で管理されています。そして、多くの10進数の小数は2進数で表現できません。

例えば十進数の「0.1」を2進数に変換すると「0.0001100110011…」となり、「0011」の部分が永遠に循環します。このような値はどこかの桁数で丸めを行う必要があるため、上記のように誤差が生じます。

また、この話はpythonに限った話ではなく、IEEE 754の2進浮動小数点形式を採用して計算していれば、同じことが起こります。


  • 追記

    float型だけではなく、int型も2進数管理されているみたいです。


対策 math.isclose()を使う

mathモジュールの関数isclose()を使うことによって、近似的な比較を行えます。

これを用いることによってfloat型の比較ができますね。

実際に使ってみましょう。


test.py

import math

def main():
x = 0
for i in range(10):
x += 0.1

if math.isclose(x, 1.0):
print("OK")
else:
print("NG")

if __name__ == "__main__":
main()



実行

$ python test.py 

OK

"OK"と表示されました!


まとめ

10進数の小数を比較するときは、誤差が生じることがあることを意識しておく必要がありそうです。

頭の片隅に置いておきましょう。