はじめに
最近Pyhonを勉強し始めた新人です。
今回は浮動小数点数floatの比較について書きたいと思います。
float型の比較
皆さん、以下のコードの出力は何だと思いますか?
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の値を確認します。
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()で表示桁数を増やすと誤差が存在するのが分かります。
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型の比較ができますね。
実際に使ってみましょう。
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進数の小数を比較するときは、誤差が生じることがあることを意識しておく必要がありそうです。
頭の片隅に置いておきましょう。