5.5.5 浮動小数点数に関する注意

10 進浮動小数点数を使うと、 10 進数表現による誤差を抑制できます (0.1 を正確に表現できるようになります); しかし、ゼロでない 桁が一定の精度を越えている場合には、演算によっては依然として値丸めによる 誤差を引き起こします。 Knuth は、十分でない計算精度の下で値丸めを伴う 浮動小数点演算を行った結果、加算の結合則や分配則における恒等性が崩れて しまう例を二つ示しています:

# Examples from Seminumerical Algorithms, Section 4.2.2.
>>> from decimal import *
>>> getcontext().prec = 8

>>> u, v, w = Decimal(11111113), Decimal(-11111111), Decimal('7.51111111')
>>> (u + v) + w
Decimal("9.5111111")
>>> u + (v + w)
Decimal("10")

>>> u, v, w = Decimal(20000), Decimal(-6), Decimal('6.0000003')
>>> (u*v) + (u*w)
Decimal("0.01")
>>> u * (v+w)
Decimal("0.0060000")

decimal モジュールでは、最下桁を失わないように十分に計算精度を 広げることで、上で問題にしたような恒等性をとりもどせます:

>>> getcontext().prec = 20
>>> u, v, w = Decimal(11111113), Decimal(-11111111), Decimal('7.51111111')
>>> (u + v) + w
Decimal("9.51111111")
>>> u + (v + w)
Decimal("9.51111111")
>>> 
>>> u, v, w = Decimal(20000), Decimal(-6), Decimal('6.0000003')
>>> (u*v) + (u*w)
Decimal("0.0060000")
>>> u * (v+w)
Decimal("0.0060000")

decimal モジュールの数体系では、NaN, sNaN, -Infinity , Infinity , および二つのゼロ、 +0-0 といった特殊な値を提供しています。

無限大 (Infinity) は Decimal('Infinity') で直接構築できます。 また、DivisionByZero をトラップせずにゼロで除算を行った 場合にも出てきます。同様に、 Overflow シグナルをトラップ しなければ、表現可能な最大の数値の制限を越えた値を丸めたときに出てきます。

無限大には符号があり (アフィン: affine であり)、算術演算に使用でき、 非常に巨大で不確定の(indeterminate)値として扱われます。例えば、無限大に 何らかの定数を加算すると、演算結果は別の無限大になります。

演算によっては結果が不確定になるものがあり、NaN を返します。 ただし、InvalidOperation シグナルをトラップするように なっていれば例外を送出します。

例えば、0/0NaN を返します。NaN は 「非数値 (not a number)」を表します。このような NaN は 暗黙のうちに生成され、一度生成されるとそれを他の計算にも流れてゆき、 関係する個々の演算全てが個別の NaN を返すようになります。 この挙動は、たまに入力値が欠けるような状況で一連の計算を行う際に 便利です -- 特定の計算に対しては無効な結果を示すフラグを立てつつ 計算を進められるからです。

一方、NaN の変種であるsNaN は関係する全ての演算 で演算後にシグナルを送出します。sNaN は、無効な演算結果 に対して特別な処理を行うために計算を停止する必要がある場合に便利です。

アンダフローの起きた計算は、符号付きのゼロ (signed zero) を返す ことがあります。符号は、より高い精度で計算を行った結果の 符号と同じになります。 符号付きゼロの大きさはやはりゼロなので、正のゼロと負のゼロは 等しいとみなされ、符号は単なる参考にすぎません。

二つの符号付きゼロが区別されているのに等価であることに加えて、 異なる精度におけるゼロの表現はまちまちなのに、値は等価と みなされるということがあります。これに慣れるには多少時間がかかります。 正規化浮動小数点表現に目が慣れてしまうと、以下の計算でゼロに 等しい値が返っているとは即座に分かりません:

>>> 1 / Decimal('Infinity')
Decimal("0E-1000000026")

ご意見やご指摘をお寄せになりたい方は、 このドキュメントについて... をご覧ください。