はじめに
Pythonで、というかコンピュータは小数の扱いが苦手です。
というのもコンピュータは2進数を基準に計算しており、小数を取り扱うのが難しいからです。
多くの場合、コンピュータは小数を扱うとき、その値の近似値を表します。
そのため、次のような計算を行うと、正確な数を計算することができません。
A = 0.33
B = 10.0
print("ans: " + str(A * B))
# ans: 3.3000000000000003
これはつまり、次のような事に注意が必要という事です。
A = 0.33
B = 10.0
if(A * B == 3.3):
print("一致")
else:
print("不一致")
# 不一致
というわけでpythonで小数を取り扱うようなライブラリを調べてみました。
decimal
decimalはpythonの標準ライブラリです。
10進数の浮動小数点のための計算を行うために設計されています。
この型を使用すると、人間の感覚に近い計算が行えます。
Doc: https://docs.python.org/ja/3/library/decimal.html
from decimal import *
x = Decimal("0.33")
x10 = x * 10
print("ans: " + str(x10))
print("type: " + str(type(x10)))
# ans: 3.30
# type: <class 'decimal.Decimal'>
Decimal型の変数はintとの計算では明示的にキャストする必要はありません。
x2 = x + 1
print(x2)
# 1.33
だたしfloat型は明示的にキャストをする必要があります。
x2 = x + 0.003
print(x2)
# TypeError: unsupported operand type(s) for +: 'decimal.Decimal' and 'float'
x2 = x + Decimal(0.003)
print(x2)
# 0.3330000000000000000624500451
あれ?
float型からDecimal型へのキャストは注意が必要
float型からDecimal型にキャストすると、うまく動作しないようです。
float型は小数の近似値をDecimal型に渡すので、その近似値を正確にDecimal型が読んでしまため、このような処理になったものと思われます。
なので、この場合はstr型を経由して、Decimal型にキャストすると良いでしょう。
print(Decimal(0.003))
# 0.003000000000000000062450045135165055398829281330108642578125
print(Decimal(str(0.003)))
# 0.003
終わりに
冒頭で失敗していた比較の演算は次のようになります。
A = Decimal(str(0.33))
B = Decimal(str(10.0))
if(float(A * B) == 3.3):
print("一致")
else:
print("不一致")
# 一致