188:浮動小数点数を比較する
差をとって丸める
浮動小数点数の演算には各種の誤差がつきものなので単に == で比較すると思ったような結果になりません。:
>>> a = 0.1 + 0.2 >>> b = 0.3 >>> a == b False
現実的な対策としては、差をとり任意の桁数で丸めて 0 と比較するとよいでしょう。 round 関数を用いて小数点以下 7 桁で丸めてみます。:
>>> a = 0.1 + 0.2 >>> b = 0.3 >>> round(a-b, 7) == 0 True
ちなみに、筆者の環境で各値を小数点以下 35 桁まで表示すると次のようになりました。:
>>> '%.35f' % a '0.30000000000000004000000000000000000' >>> '%.35f' % b '0.29999999999999999000000000000000000' >>> '%.35f' % (a - b) '0.00000000000000005551115123125782700' >>> '%.35f' % round(a - b, 7) '0.00000000000000000000000000000000000'
参考
Unittest.py より TestCase クラスの failUnlessAlmostEqual メソッド。
first と second を places で与えた小数位で値を丸めて差分を計算し、ゼロと比較することで、近似的に等価であるかどうかをテストします。:
def failUnlessAlmostEqual(self, first, second, places=7, msg=None):
if round(second-first, places) != 0:
raise self.failureException, \
(msg or '%r != %r within %r places' % (first, second, places))
レシピを書くにあたりパクっ…参考にしています
Decimal - 10 進浮動小数点数
レシピの趣旨に反しますが。 2 進浮動小数点数の 2 進ゆえのまるめ誤差に悩まされたくなければ 10 進浮動小数点数を用いるという手段があります。 decimal モジュールの Decimal クラスがそうです。:
>>> from decimal import Decimal
>>> a = Decimal('0.1') + Decimal('0.2')
>>> b = Decimal('0.3')
>>> a == b
True
日ごろ用いている算数がほぼ通用するのはありがたいものです。とはいえ、有限の浮動小数点数には違いないので 1 / 3
といった無限小数をうまく表現できるわけではありませんし、精度を超えた情報の保持も不可能です。
比較の際、精度内での計算に始終していたのでなければ 2 進浮動小数点数同様の注意が必要です。:
>>> Decimal(1) == 1
True
>>> Decimal(1) / Decimal(3)
Decimal("0.3333333333333333333333333333")
>>> Decimal(1) / Decimal(3) * Decimal(3) == 1 # 1/3*3 と 1 は 10 進浮動小数点数でも等しくならない
False
>>>
>>> x = Decimal(10000000000000000)
>>> y = Decimal('0.000000000000001')
>>> x == x + y # 加えた値と桁が違いすぎて無視される
True
なお、 decimal モジュールでは計算精度、算術コンテキスト、シグナルを Python プログラマ側で制御できるところも魅力となっています。 詳しくは ライブラリリファレンス decimal を参照してください。
有理数
有理数を用いることで 1 / 3 などの正確な表現が可能となります。 192:有理数を使う を参照してください。