9.4. decimal --- 十進固定及び浮動小数点数の算術演算

ソースコード: Lib/decimal.py


decimal モジュールは正確に丸められた十進浮動小数点算術をサポートします。 decimal には、 float データ型に比べて、以下のような利点があります:

  • 「(Decimal は) 人々を念頭にデザインされた浮動小数点モデルを元にしており、必然的に最も重要な指針があります -- コンピュータは人々が学校で習った算術と同じように動作する算術を提供しなければならない」 -- 十進数演算仕様より。

  • 十進数を正確に表現できます。 1.12.2 のような数は、二進数の浮動小数点型では正しく表現できません。エンドユーザは普通、 二進数における 1.1 + 2.2 の近似値が 3.3000000000000003 だからといって、そのように表示してほしいとは考えないものです。

  • 値の正確さは算術にも及びます。十進の浮動小数点による計算では、 0.1 + 0.1 + 0.1 - 0.3 は厳密にゼロに等しくなります。 二進浮動小数点では 5.5511151231257827e-017 になってしまいます。ゼロに近い値とはいえ、この誤差は数値間の等価性テストの信頼性を阻害します。また、誤差が蓄積されることもあります。こうした理由から、数値間の等価性を厳しく保たなければならないようなアプリケーションを考えるなら、十進数による数値表現が望ましいということになります。

  • decimal モジュールでは、有効桁数の表記が取り入れられており、例えば 1.30 + 1.202.50 になります。すなわち、末尾のゼロは有効数字を示すために残されます。こうした仕様は通貨計算を行うアプリケーションでは慣例です。乗算の場合、「教科書的な」アプローチでは、乗算の被演算子すべての桁数を使います。例えば、 1.3 * 1.21.56 になり、 1.30 * 1.201.5600 になります。

  • ハードウェアによる 2 進浮動小数点表現と違い、decimal モジュールでは計算精度をユーザが変更できます(デフォルトでは 28 桁です)。この桁数はほとんどの問題解決に十分な大きさです:

    >>> from decimal import *
    >>> getcontext().prec = 6
    >>> Decimal(1) / Decimal(7)
    Decimal('0.142857')
    >>> getcontext().prec = 28
    >>> Decimal(1) / Decimal(7)
    Decimal('0.1428571428571428571428571429')
    
  • 二進と十進の浮動小数点は、いずれも広く公開されている標準仕様のもとに実装されています。組み込みの浮動小数点型では、標準仕様で提唱されている機能のほんのささやかな部分を利用できるにすぎませんが、decimal では標準仕様が要求している全ての機能を利用できます。必要に応じて、プログラマは値の丸めやシグナル処理を完全に制御できます。この中には全ての不正確な操作を例外でブロックして正確な算術を遵守させるオプションもあります。

  • decimal モジュールは「偏見なく、正確な丸めなしの十進算術(固定小数点算術と呼ばれることもある)と丸めありの浮動小数点数算術」(十進数演算仕様より引用)をサポートするようにデザインされました。

このモジュールは、十進数型、算術コンテキスト (context for arithmetic)、そしてシグナル (signal) という三つの概念を中心に設計されています。

十進数型は変更不能です。これは符号、係数部、そして指数を持ちます。有効桁数を残すために、仮数部の末尾にあるゼロは切り詰められません。 decimal では、 Infinity, -Infinity, および NaN といった特殊な値も定義されています。標準仕様では -0+0 も区別します。

算術コンテキストとは、精度や値丸めの規則、指数部の制限を決めている環境です。この環境では、演算結果を表すためのフラグや、演算上発生した特定のシグナルを例外として扱うかどうかを決めるトラップイネーブラも定義しています。丸め規則には ROUND_CEILING, ROUND_DOWN, ROUND_FLOOR, ROUND_HALF_DOWN, ROUND_HALF_EVEN, ROUND_HALF_UP, ROUND_UP, および ROUND_05UP があります。

シグナルとは、演算の過程で生じる例外的条件です。個々のシグナルは、アプリケーションそれぞれの要求に従って、無視されたり、単なる情報とみなされたり、例外として扱われたりします。 decimal モジュールには、 Clamped, InvalidOperation, DivisionByZero, Inexact, Rounded, Subnormal, Overflow, Underflow, および FloatOperation といったシグナルがあります。

各シグナルには、フラグとトラップイネーブラがあります。演算上何らかのシグナルに遭遇すると、フラグは 1 にセットされます。このとき、もしトラップイネーブラが 1 にセットされていれば、例外を送出します。フラグの値は膠着型 (sticky) なので、演算によるフラグの変化をモニタしたければ、予めフラグをリセットしておかなければなりません。

参考

9.4.1. クイックスタートチュートリアル

普通、 decimal を使うときには、モジュールをインポートし、現在の演算コンテキストを getcontext() で調べ、必要なら、精度、丸め、有効なトラップを設定します:

>>> from decimal import *
>>> getcontext()
Context(prec=28, rounding=ROUND_HALF_EVEN, Emin=-999999, Emax=999999,
        capitals=1, clamp=0, flags=[], traps=[Overflow, DivisionByZero,
        InvalidOperation])

>>> getcontext().prec = 7       # Set a new precision

Decimal インスタンスは、整数、文字列、浮動小数点数、またはタプルから構成できます。整数や浮動小数点数からの構成は、整数や浮動小数点数の値を正確に変換します。 Decimal は "非数 (Not a Number)" を表す NaN や正負の Infinity (無限大)、 -0 といった特殊な値も表現できます:

>>> getcontext().prec = 28
>>> Decimal(10)
Decimal('10')
>>> Decimal('3.14')
Decimal('3.14')
>>> Decimal(3.14)
Decimal('3.140000000000000124344978758017532527446746826171875')
>>> Decimal((0, (3, 1, 4), -2))
Decimal('3.14')
>>> Decimal(str(2.0 ** 0.5))
Decimal('1.4142135623730951')
>>> Decimal(2) ** Decimal('0.5')
Decimal('1.414213562373095048801688724')
>>> Decimal('NaN')
Decimal('NaN')
>>> Decimal('-Infinity')
Decimal('-Infinity')

FloatOperation シグナルがトラップされる場合、コンストラクタや順序比較において誤って decimal と float が混ざると、例外が送出されます:

>>> c = getcontext()
>>> c.traps[FloatOperation] = True
>>> Decimal(3.14)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
decimal.FloatOperation: [<class 'decimal.FloatOperation'>]
>>> Decimal('3.5') < 3.7
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
decimal.FloatOperation: [<class 'decimal.FloatOperation'>]
>>> Decimal('3.5') == 3.5
True

バージョン 3.3 で追加.

新たな Decimal の有効桁数は入力の桁数だけで決まります。演算コンテキストにおける精度や値丸めの設定が影響するのは算術演算の間だけです。

>>> getcontext().prec = 6
>>> Decimal('3.0')
Decimal('3.0')
>>> Decimal('3.1415926535')
Decimal('3.1415926535')
>>> Decimal('3.1415926535') + Decimal('2.7182818285')
Decimal('5.85987')
>>> getcontext().rounding = ROUND_UP
>>> Decimal('3.1415926535') + Decimal('2.7182818285')
Decimal('5.85988')

C バージョンの内部制限を超えた場合、decimal の構成は InvalidOperation を送出します:

>>> Decimal("1e9999999999999999999")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
decimal.InvalidOperation: [<class 'decimal.InvalidOperation'>]

バージョン 3.3 で変更.

decimal はほとんどの場面で Python の他の機能とうまくやりとりできます。decimal 浮動小数点の空飛ぶサーカス (flying circus) をお見せしましょう:

>>> data = list(map(Decimal, '1.34 1.87 3.45 2.35 1.00 0.03 9.25'.split()))
>>> max(data)
Decimal('9.25')
>>> min(data)
Decimal('0.03')
>>> sorted(data)
[Decimal('0.03'), Decimal('1.00'), Decimal('1.34'), Decimal('1.87'),
 Decimal('2.35'), Decimal('3.45'), Decimal('9.25')]
>>> sum(data)
Decimal('19.29')
>>> a,b,c = data[:3]
>>> str(a)
'1.34'
>>> float(a)
1.34
>>> round(a, 1)
Decimal('1.3')
>>> int(a)
1
>>> a * 5
Decimal('6.70')
>>> a * b
Decimal('2.5058')
>>> c % a
Decimal('0.77')

いくつかの数学的関数も Decimal には用意されています:

>>> getcontext().prec = 28
>>> Decimal(2).sqrt()
Decimal('1.414213562373095048801688724')
>>> Decimal(1).exp()
Decimal('2.718281828459045235360287471')
>>> Decimal('10').ln()
Decimal('2.302585092994045684017991455')
>>> Decimal('10').log10()
Decimal('1')

quantize() メソッドは位を固定して数値を丸めます。このメソッドは、結果を固定の桁数で丸めることがよくある、金融アプリケーションで便利です:

>>> Decimal('7.325').quantize(Decimal('.01'), rounding=ROUND_DOWN)
Decimal('7.32')
>>> Decimal('7.325').quantize(Decimal('1.'), rounding=ROUND_UP)
Decimal('8')

前述のように、 getcontext() 関数を使うと現在の演算コンテキストにアクセスでき、設定を変更できます。ほとんどのアプリケーションはこのアプローチで十分です。

より高度な作業を行う場合、 Context() コンストラクタを使って別の演算コンテキストを作っておくと便利なことがあります。別の演算コンテキストをアクティブにしたければ、 setcontext() を使います。

decimal モジュールでは、標準仕様に従って、すぐ利用できる二つの標準コンテキスト、 BasicContext および ExtendedContext を提供しています。前者はほとんどのトラップが有効になっており、とりわけデバッグの際に便利です:

>>> myothercontext = Context(prec=60, rounding=ROUND_HALF_DOWN)
>>> setcontext(myothercontext)
>>> Decimal(1) / Decimal(7)
Decimal('0.142857142857142857142857142857142857142857142857142857142857')

>>> ExtendedContext
Context(prec=9, rounding=ROUND_HALF_EVEN, Emin=-999999, Emax=999999,
        capitals=1, clamp=0, flags=[], traps=[])
>>> setcontext(ExtendedContext)
>>> Decimal(1) / Decimal(7)
Decimal('0.142857143')
>>> Decimal(42) / Decimal(0)
Decimal('Infinity')

>>> setcontext(BasicContext)
>>> Decimal(42) / Decimal(0)
Traceback (most recent call last):
  File "<pyshell#143>", line 1, in -toplevel-
    Decimal(42) / Decimal(0)
DivisionByZero: x / 0

演算コンテキストには、演算中に遭遇した例外的状況をモニタするためのシグナルフラグがあります。フラグが一度セットされると、明示的にクリアするまで残り続けます。そのため、フラグのモニタを行いたいような演算の前には clear_flags() メソッドでフラグをクリアしておくのがベストです。

>>> setcontext(ExtendedContext)
>>> getcontext().clear_flags()
>>> Decimal(355) / Decimal(113)
Decimal('3.14159292')
>>> getcontext()
Context(prec=9, rounding=ROUND_HALF_EVEN, Emin=-999999, Emax=999999,
        capitals=1, clamp=0, flags=[Inexact, Rounded], traps=[])

flags エントリから、 Pi の有理数による近似値が丸められた (コンテキスト内で決められた精度を超えた桁数が捨てられた) ことと、計算結果が厳密でない (無視された桁の値に非ゼロのものがあった) ことがわかります。

コンテキストの traps フィールドに入っている辞書を使うと、個々のトラップをセットできます:

>>> setcontext(ExtendedContext)
>>> Decimal(1) / Decimal(0)
Decimal('Infinity')
>>> getcontext().traps[DivisionByZero] = 1
>>> Decimal(1) / Decimal(0)
Traceback (most recent call last):
  File "<pyshell#112>", line 1, in -toplevel-
    Decimal(1) / Decimal(0)
DivisionByZero: x / 0

ほとんどのプログラムでは、開始時に一度だけ現在の演算コンテキストを修正します。また、多くのアプリケーションでは、データから Decimal への変換はループ内で一度だけキャストして行います。コンテキストを設定し、 Decimal オブジェクトを生成できたら、ほとんどのプログラムは他の Python 数値型と全く変わらないかのように Decimal を操作できます。

9.4.2. Decimal オブジェクト

class decimal.Decimal(value="0", context=None)

value に基づいて新たな Decimal オブジェクトを構築します。

value can be an integer, string, tuple, float, or another Decimal object. If no value is given, returns Decimal('0'). If value is a string, it should conform to the decimal numeric string syntax after leading and trailing whitespace characters are removed:

sign           ::=  '+' | '-'
digit          ::=  '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'
indicator      ::=  'e' | 'E'
digits         ::=  digit [digit]...
decimal-part   ::=  digits '.' [digits] | ['.'] digits
exponent-part  ::=  indicator [sign] digits
infinity       ::=  'Infinity' | 'Inf'
nan            ::=  'NaN' [digits] | 'sNaN' [digits]
numeric-value  ::=  decimal-part [exponent-part] | infinity
numeric-string ::=  [sign] numeric-value | [sign] nan

他の Unicode 数字も上の digit の場所に使うことができます。つまり各書記体系における(アラビア-インド系やデーヴァナーガリーなど)の数字や、全角数字0('\uff10')から9('\uff19')までなどです。

valuetuple にする場合、タプルは三つの要素を持ち、それぞれ符号 (正なら 0 、負なら 1)、仮数部を表す数字の tuple 、そして指数を表す整数でなければなりません。例えば、 Decimal((0, (1, 4, 1, 4), -3))Decimal('1.414') を返します。

valuefloat にする場合、二進浮動小数点数値が損失なく正確に等価な Decimal に変換されます。この変換はしばしば 53 桁以上の精度を要求します。例えば、 Decimal(float('1.1'))Decimal('1.100000000000000088817841970012523233890533447265625') に変換されます。

context の精度 (precision) は、記憶される桁数には影響しません。桁数は value に指定した桁数だけから決定されます。例えば、演算コンテキストに指定された精度が 3 桁しかなくても、Decimal('3.00000') は 5 つのゼロを全て記憶します。

context 引数の目的は、 value が正しくない形式の文字列であった場合に行う処理を決めることにあります; 演算コンテキストが InvalidOperation をトラップするようになっていれば、例外を送出します。それ以外の場合には、コンストラクタは値が NaNDecimal を返します。

一度生成すると、 Decimal オブジェクトは変更不能 (immutable) になります。

バージョン 3.2 で変更: コンストラクタに対する引数に float インスタンスも許されるようになりました。

バージョン 3.3 で変更: FloatOperation トラップがセットされていた場合 float 引数は例外を送出します。デフォルトでトラップはオフです。

十進浮動小数点オブジェクトは、 floatint のような他の組み込み型と多くの点で似ています。通常の数学演算や特殊メソッドを適用できます。また、 Decimal オブジェクトはコピーでき、pickle 化でき、print で出力でき、辞書のキーにでき、集合の要素にでき、比較、保存、他の型 (floatint) への型強制を行えます。

十進オブジェクトの算術演算と整数や浮動小数点数の算術演算には少々違いがあります。十進オブジェクトに対して剰余演算を適用すると、計算結果の符号は除数の符号ではなく 被除数 の符号と一致します:

>>> (-7) % 4
1
>>> Decimal(-7) % Decimal(4)
Decimal('-3')

整数除算演算子 // も同様に、実際の商の切り捨てではなく (0に近付くように丸めた) 整数部分を返します。そうすることで通常の恒等式 x == (x // y) * y + x % y が保持されます:

>>> -7 // 4
-2
>>> Decimal(-7) // Decimal(4)
Decimal('-1')

演算子 % と演算子 // は (それぞれ) 仕様にあるような 剰余 操作と 整数除算 操作を実装しています。

Decimal オブジェクトは一般に、算術演算で浮動小数点数や fractions.Fraction オブジェクトと組み合わせることができません。例えば、 Decimalfloat を足そうとすると、 TypeError が送出されます。ただし、Python の比較演算子を使って Decimal インスタンス x と別の数 y を比較することができます。これにより、異なる型の数間の等価比較の際に、紛らわしい結果を避けます。

バージョン 3.2 で変更: Decimal インスタンスと他の数値型が混在する比較が完全にサポートされるようになりました。

こうした標準的な数値型の特性の他に、十進浮動小数点オブジェクトには様々な特殊メソッドがあります:

adjusted()

仮数の先頭の一桁だけが残るように右側の数字を追い出す桁シフトを行い、その結果の指数部を返します: Decimal('321e+5').adjusted() は 7 を返します。最上桁の小数点からの相対位置を調べる際に使います。

as_tuple()

数値を表現するための名前付きタプル(named tuple): DecimalTuple(sign, digittuple, exponent) を返します。

canonical()

引数の標準的(canonical)エンコーディングを返します。現在のところ、 Decimal インスタンスのエンコーディングは常に標準的なので、この操作は引数に手を加えずに返します。

compare(other, context=None)

二つの Decimal インスタンスの値を比較します。 compare() は Decimal インスタンスを返し、被演算子のどちらかが NaN ならば結果は NaN です:

a or b is a NaN  ==> Decimal('NaN')
a < b            ==> Decimal('-1')
a == b           ==> Decimal('0')
a > b            ==> Decimal('1')
compare_signal(other, context=None)

この演算は compare() とほとんど同じですが、全ての NaN がシグナルを送るところが異なります。すなわち、どちらの比較対象も発信 (signaling) NaN でないならば無言 (quiet) NaN である比較対象があたかも発信 NaN であるかのように扱われます。

compare_total(other, context=None)

二つの対象を数値によらず抽象表現によって比較します。 compare() に似ていますが、結果は Decimal に全順序を与えます。この順序づけによると、数値的に等しくても異なった表現を持つ二つの Decimal インスタンスの比較は等しくなりません:

>>> Decimal('12.0').compare_total(Decimal('12'))
Decimal('-1')

無言 NaN と発信 NaN もこの全順序に位置付けられます。この関数の結果は、もし比較対象が同じ表現を持つならば Decimal('0') であり、一つめの比較対象が二つめより下位にあれば Decimal('-1')、上位にあれば Decimal('1') です。全順序の詳細については仕様を参照してください。

この演算はコンテキストに影響されず、静かです。すなわち、フラグは変更されず、丸めは行われません。例外として、2番目の比較対象の厳密な変換ができない場合、C バージョンのライブラリでは InvalidOperation 例外を送出するかもしれません。

compare_total_mag(other, context=None)

二つの対象を compare_total() のように数値によらず抽象表現によって比較しますが、両者の符号を無視します。 x.compare_total_mag(y)x.copy_abs().compare_total(y.copy_abs()) と等価です。

この演算はコンテキストに影響されず、静かです。すなわち、フラグは変更されず、丸めは行われません。例外として、2番目の比較対象の厳密な変換ができない場合、C バージョンのライブラリでは InvalidOperation 例外を送出するかもしれません。

conjugate()

self を返すだけです。このメソッドは十進演算仕様に適合するためだけのものです。

copy_abs()

引数の絶対値を返します。この演算はコンテキストに影響されず、静かです。すなわち、フラグは変更されず、丸めは行われません。

copy_negate()

引数の符号を変えて返します。この演算はコンテキストに影響されず、静かです。すなわち、フラグは変更されず、丸めは行われません。

copy_sign(other, context=None)

最初の演算対象のコピーに二つめと同じ符号を付けて返します。たとえば:

>>> Decimal('2.3').copy_sign(Decimal('-1.5'))
Decimal('-2.3')

この演算はコンテキストに影響されず、静かです。すなわち、フラグは変更されず、丸めは行われません。例外として、2番目の比較対象の厳密な変換ができない場合、C バージョンのライブラリでは InvalidOperation 例外を送出するかもしれません。

exp(context=None)

与えられた数での(自然)指数関数 e**x の値を返します。結果は ROUND_HALF_EVEN 丸めモードで正しく丸められます。

>>> Decimal(1).exp()
Decimal('2.718281828459045235360287471')
>>> Decimal(321).exp()
Decimal('2.561702493119680037517373933E+139')
from_float(f)

浮動小数点数を正確に小数に変換するクラスメソッドです。

なお、Decimal.from_float(0.1)Decimal('0.1') と同じではありません。0.1 は二進浮動小数点数で正確に表せないので、その値は表現できる最も近い値、0x1.999999999999ap-4 として記憶されます。浮動小数点数での等価な値は 0.1000000000000000055511151231257827021181583404541015625 です。

注釈

Python 3.2 以降では、 Decimal インスタンスは float から直接構成できるようになりました。

>>> Decimal.from_float(0.1)
Decimal('0.1000000000000000055511151231257827021181583404541015625')
>>> Decimal.from_float(float('nan'))
Decimal('NaN')
>>> Decimal.from_float(float('inf'))
Decimal('Infinity')
>>> Decimal.from_float(float('-inf'))
Decimal('-Infinity')

バージョン 3.1 で追加.

fma(other, third, context=None)

融合積和(fused multiply-add)です。self*other+third を途中結果の積 self*other で丸めを行わずに計算して返します。

>>> Decimal(2).fma(3, 5)
Decimal('11')
is_canonical()

引数が標準的(canonical)ならば True を返し、そうでなければ False を返します。現在のところ、 Decimal のインスタンスは常に標準的なのでこのメソッドの結果はいつでも True です。

is_finite()

引数が有限の数値ならば True を、無限大か NaN ならば False を返します。

is_infinite()

引数が正または負の無限大ならば True を、そうでなければ False を返します。

is_nan()

引数が (無言か発信かは問わず) NaN であれば True を、そうでなければ False を返します。

is_normal(context=None)

引数が 正規(normal) の有限数値ならば True を返します。引数がゼロ、非正規(subnormal)、無限大または NaN であれば False を返します。

is_qnan()

引数が無言 NaN であれば True を、そうでなければ False を返します。

is_signed()

引数に負の符号がついていれば True を、そうでなければ False を返します。注意すべきはゼロや NaN なども符号を持ち得ることです。

is_snan()

引数が発信 NaN であれば True を、そうでなければ False を返します。

is_subnormal(context=None)

引数が非正規数(subnormal)であれば True を、そうでなければ False を返します。

is_zero()

引数が(正または負の)ゼロであれば True を、そうでなければ False を返します。

ln(context=None)

演算対象の自然対数(底 e の対数)を返します。結果は ROUND_HALF_EVEN 丸めモードで正しく丸められます。

log10(context=None)

演算対象の底 10 の対数を返します。結果は ROUND_HALF_EVEN 丸めモードで正しく丸められます。

logb(context=None)

非零の数値については、 Decimal インスタンスとして調整された指数を返します。演算対象がゼロだった場合、 Decimal('-Infinity') が返され DivisionByZero フラグが送出されます。演算対象が無限大だった場合、 Decimal('Infinity') が返されます。

logical_and(other, context=None)

logical_and() は二つの 論理引数 (論理引数 参照)を取る論理演算です。結果は二つの引数の数字ごとの and です。

logical_invert(context=None)

logical_invert() は論理演算です。結果は引数の数字ごとの反転です。

logical_or(other, context=None)

logical_or() は二つの 論理引数 (論理引数 参照)を取る論理演算です。結果は二つの引数の数字ごとの or です。

logical_xor(other, context=None)

logical_xor() は二つの 論理引数 (論理引数 参照)を取る論理演算です。結果は二つの引数の数字ごとの排他的論理和です。

max(other, context=None)

max(self, other) と同じですが、値を返す前に現在のコンテキストに即した丸め規則を適用します。また、 NaN に対して、(コンテキストの設定と、発信か無言どちらのタイプであるかに応じて) シグナルを発行するか無視します。

max_mag(other, context=None)

max() メソッドに似ていますが、比較は絶対値で行われます。

min(other, context=None)

min(self, other) と同じですが、値を返す前に現在のコンテキストに即した丸め規則を適用します。また、 NaN に対して、(コンテキストの設定と、発信か無言どちらのタイプであるかに応じて) シグナルを発行するか無視します。

min_mag(other, context=None)

min() メソッドに似ていますが、比較は絶対値で行われます。

next_minus(context=None)

与えられたコンテキスト(またはコンテキストが渡されなければ現スレッドのコンテキスト)において表現可能な、操作対象より小さい最大の数を返します。

next_plus(context=None)

与えられたコンテキスト(またはコンテキストが渡されなければ現スレッドのコンテキスト)において表現可能な、操作対象より大きい最小の数を返します。

next_toward(other, context=None)

二つの比較対象が等しくなければ、一つめの対象に最も近く二つめの対象へ近付く方向の数を返します。もし両者が数値的に等しければ、二つめの対象の符号を採った一つめの対象のコピーを返します。

normalize(context=None)

数値を正規化 (normalize) して、右端に連続しているゼロを除去し、 Decimal('0') と同じ結果はすべて Decimal('0e0') に変換します。等価クラスの属性から基準表現を生成する際に用います。たとえば、 Decimal('32.100')Decimal('0.321000e+2') の正規化は、いずれも同じ値 Decimal('32.1') になります。

number_class(context=None)

操作対象の クラス を表す文字列を返します。返されるのは以下の10種類のいずれかです。

  • "-Infinity", 負の無限大であることを示します。
  • "-Normal", 負の通常数であることを示します。
  • "-Subnormal", 負の非正規数であることを示します。
  • "-Zero", 負のゼロであることを示します。
  • "+Zero", 正のゼロであることを示します。
  • "+Subnormal", 正の非正規数であることを示します。
  • "+Normal", 正の通常数であることを示します。
  • "+Infinity", 正の無限大であることを示します。
  • "NaN", 無言 (quiet) NaN (Not a Number) であることを示します。
  • "sNaN", 発信(signaling) NaN であることを示します。
quantize(exp, rounding=None, context=None)

二つ目の操作対象と同じ指数を持つように丸めを行った、一つめの操作対象と等しい値を返します。

>>> Decimal('1.41421356').quantize(Decimal('1.000'))
Decimal('1.414')

他の操作と違い、打ち切り(quantize)操作後の係数の長さが精度を越えた場合には、 InvalidOperation がシグナルされます。これによりエラー条件がない限り打ち切られた指数が常に右側の引数と同じになることが保証されます。

同様に、他の操作と違い、quantize は Underflow を、たとえ結果が非正規になったり不正確になったとしても、シグナルしません。

二つ目の演算対象の指数が一つ目のそれよりも大きければ丸めが必要かもしれません。この場合、丸めモードは以下のように決められます。rounding 引数が与えられていればそれが使われます。そうでなければ context 引数で決まります。どちらの引数も渡されなければ現在のスレッドのコンテキストの丸めモードが使われます。

処理結果の指数が Emax よりも大きい場合や Etiny よりも小さい場合にエラーが返されます。

radix()

Decimal(10) つまり Decimal クラスがその全ての算術を実行する基数を返します。仕様との互換性のために取り入れられています。

remainder_near(other, context=None)

selfother で割った剰余を返します。これは self % other とは違って、剰余の絶対値を小さくするように符号が選ばれます。より詳しく言うと、nself / other の正確な値に最も近い整数としたときの self - n * other が返り値になります。最も近い整数が2つある場合には偶数のものが選ばれます。

結果が0になる場合の符号は self の符号と同じになります。

>>> Decimal(18).remainder_near(Decimal(10))
Decimal('-2')
>>> Decimal(25).remainder_near(Decimal(10))
Decimal('5')
>>> Decimal(35).remainder_near(Decimal(10))
Decimal('-5')
rotate(other, context=None)

一つ目の演算対象の数字を二つ目で指定された量だけ巡回(rotate)した結果を返します。二つめの演算対象は -precision から precision までの範囲の整数でなければなりません。この二つ目の演算対象の絶対値を何桁ずらすかを決めます。そしてもし正の数ならば巡回の方向は左に、そうでなければ右になります。一つ目の演算対象の仮数部は必要ならば精度いっぱいまでゼロで埋められます。符号と指数は変えられません。

same_quantum(other, context=None)

selfother が同じ指数を持っているか、あるいは双方とも NaN である場合に真を返します。

この演算はコンテキストに影響されず、静かです。すなわち、フラグは変更されず、丸めは行われません。例外として、2番目の比較対象の厳密な変換ができない場合、C バージョンのライブラリでは InvalidOperation 例外を送出するかもしれません。

scaleb(other, context=None)

二つ目の演算対象で調整された指数の一つ目の演算対象を返します。同じことですが、一つめの演算対象を 10**other 倍したものを返します。二つ目の演算対象は整数でなければなりません。

shift(other, context=None)

一つ目の演算対象の数字を二つ目で指定された量だけシフトした結果を返します。二つ目の演算対象は -precision から precision までの範囲の整数でなければなりません。この二つ目の演算対象の絶対値が何桁ずらすかを決めます。そしてもし正の数ならばシフトの方向は左に、そうでなければ右になります。一つ目の演算対象の係数は必要ならば精度いっぱいまでゼロで埋められます。符号と指数は変えられません。

sqrt(context=None)

引数の平方根を最大精度で求めます。

to_eng_string(context=None)

文字列に変換します。指数が必要なら工学表記が使われます。

工学表記法では指数は 3 の倍数になります。これにより、基数の小数部には最大で 3 桁までの数字が残されるとともに、末尾に 1 つまたは 2 つの 0 の付加が必要とされるかもしれません。

たとえば、Decimal('123E+1')Decimal('1.23E+3') に変換されます。

to_integral(rounding=None, context=None)

to_integral_value() メソッドと同じです。to_integral の名前は古いバージョンとの互換性のために残されています。

to_integral_exact(rounding=None, context=None)

最近傍の整数に値を丸め、丸めが起こった場合には Inexact または Rounded のシグナルを適切に出します。丸めモードは以下のように決められます。 rounding 引数が与えられていればそれが使われます。そうでなければ context 引数で決まります。どちらの引数も渡されなければ現在のスレッドのコンテキストの丸めモードが使われます。

to_integral_value(rounding=None, context=None)

InexactRounded といったシグナルを出さずに最近傍の整数に値を丸めます。 rounding が指定されていれば適用されます; それ以外の場合、値丸めの方法は context の設定か現在のコンテキストの設定になります。

9.4.2.1. 論理引数

logical_and(), logical_invert(), logical_or(), および logical_xor() メソッドはその引数が 論理引数 であると想定しています。 論理引数 とは Decimal インスタンスで指数と符号は共にゼロであり、各桁の数字が 01 であるものです。

9.4.3. Context オブジェクト

コンテキスト (context) とは、算術演算における環境設定です。コンテキストは計算精度を決定し、値丸めの方法を設定し、シグナルのどれが例外になるかを決め、指数の範囲を制限しています。

多重スレッドで処理を行う場合には各スレッドごとに現在のコンテキストがあり、 getcontext()setcontext() といった関数でアクセスしたり設定変更できます:

decimal.getcontext()

アクティブなスレッドの現在のコンテキストを返します。

decimal.setcontext(c)

アクティブなスレッドのコンテキストを c に設定します。

with 文と localcontext() 関数を使って実行するコンテキストを一時的に変更することもできます。

decimal.localcontext(ctx=None)

with 文の入口でアクティブなスレッドのコンテキストを ctx のコピーに設定し、with 文を抜ける時に元のコンテキストに復旧する、コンテキストマネージャを返します。コンテキストが指定されなければ、現在のコンテキストのコピーが使われます。

たとえば、以下のコードでは精度を42桁に設定し、計算を実行し、そして元のコンテキストに復帰します:

from decimal import localcontext

with localcontext() as ctx:
    ctx.prec = 42   # Perform a high precision calculation
    s = calculate_something()
s = +s  # Round the final result back to the default precision

新たなコンテキストは、以下で説明する Context コンストラクタを使って生成できます。その他にも、 decimal モジュールでは作成済みのコンテキストを提供しています:

class decimal.BasicContext

汎用十進演算仕様で定義されている標準コンテキストの一つです。精度は 9 桁に設定されています。丸め規則は ROUND_HALF_UP です。すべての演算結果フラグはクリアされています。 Inexact, Rounded, Subnormal を除く全ての演算エラートラップが有効 (例外として扱う) になっています。

多くのトラップが有効になっているので、デバッグの際に便利なコンテキストです。

class decimal.ExtendedContext

汎用十進演算仕様で定義されている標準コンテキストの一つです。精度は 9 桁に設定されています。丸め規則は ROUND_HALF_EVEN です。すべての演算結果フラグはクリアされています。トラップは全て無効(演算中に一切例外を送出しない) になっています。

トラップが無効になっているので、エラーの伴う演算結果を NaNInfinity にし、例外を送出しないようにしたいアプリケーションに向いたコンテキストです。このコンテキストを使うと、他の場合にはプログラムが停止してしまうような状況があっても実行を完了させられます。

class decimal.DefaultContext

Context コンストラクタが新たなコンテキストを作成するさいに雛形にするコンテキストです。このコンテキストのフィールド (精度の設定など) を変更すると、 Context コンストラクタが生成する新たなコンテキストに影響を及ぼします。

このコンテキストは、主に多重スレッド環境で便利です。スレッドを開始する前に何らかのフィールドを変更しておくと、システム全体のデフォルト設定に効果を及ぼすことができます。スレッドを開始した後にフィールドを変更すると、競合条件を抑制するためにスレッドを同期化しなければならないので、推奨しません。

単一スレッドの環境では、このコンテキストを使わないよう薦めます。下で述べるように明示的にコンテキストを作成してください。

デフォルトの値は、 prec=28, rounding=ROUND_HALF_EVEN で、トラップ Overflow, InvalidOperation, および DivisionByZero が有効になっています。

上に挙げた三つのコンテキストに加え、 Context コンストラクタを使って新たなコンテキストを生成できます。

class decimal.Context(prec=None, rounding=None, Emin=None, Emax=None, capitals=None, clamp=None, flags=None, traps=None)

新たなコンテキストを生成します。あるフィールドが定義されていないか None であれば、 DefaultContext からデフォルト値をコピーします。 flags フィールドが設定されていいか None の場合には、全てのフラグがクリアされます。

prec フィールドは範囲 [1, MAX_PREC] 内の整数で、コンテキストにおける算術演算の計算精度を設定します。

rounding オプションは、節 丸めモード で挙げられる定数の一つです。

traps および flags フィールドには、セットしたいシグナルを列挙します。一般的に、新たなコンテキストを作成するときにはトラップだけを設定し、フラグはクリアしておきます。

Emin および Emax フィールドは、許容する指数の外限を指定する整数です。 Emin は範囲 [MIN_EMIN, 0] 内で、 Emax は範囲 [0, MAX_EMAX] 内でなければなりません。

capitals フィールドは 0 または 1 (デフォルト) にします。 1 に設定すると、指数記号を大文字 E で出力します。それ以外の場合には Decimal('6.02e+23') のように e を使います。

clamp フィールドは、 0 (デフォルト) または 1 です。 1 に設定されると、このコンテキストにおける Decimal インスタンスの指数 e は厳密に範囲 Emin - prec + 1 <= e <= Emax - prec + 1 に制限されます。 clamp0 なら、それより弱い条件が支配します: 調整された Decimal インスタンスの指数は最大で Emax です。 clamp1 なら、大きな正規数は、可能なら、指数が減らされ、対応する数の 0 が係数に加えられ、指数の制約に合わせられます; これは数の値を保存しますが、有効な末尾の 0 に関する情報を失います。例えば:

>>> Context(prec=6, Emax=999, clamp=1).create_decimal('1.23e999')
Decimal('1.23000E+999')

clamp の値 1 は、IEEE 754 で規定された固定幅十進交換形式と互換にできます。

Context クラスでは、いくつかの汎用のメソッドの他、現在のコンテキストで算術演算を直接行うためのメソッドを数多く定義しています。加えて、 Decimal の各メソッドについて(adjusted() および as_tuple() メソッドを例外として)対応する Context のメソッドが存在します。たとえば、 Context インスタンス CDecimal インスタンス x に対して、 C.exp(x)x.exp(context=C) と等価です。それぞれの Context メソッドは、Decimal インスタンスが受け付けられるところならどこでも、Python の整数 (int のインスタンス) を受け付けます。

clear_flags()

フラグを全て 0 にリセットします。

clear_traps()

トラップを全て 0 にリセットします。

バージョン 3.3 で追加.

copy()

コンテキストの複製を返します。

copy_decimal(num)

Decimal インスタンス num のコピーを返します。

create_decimal(num)

self をコンテキストとする新たな Decimal インスタンスを num から生成します。 Decimal コンストラクタと違い、数値を変換する際にコンテキストの精度、値丸め方法、フラグ、トラップを適用します。

定数値はしばしばアプリケーションの要求よりも高い精度を持っているため、このメソッドが役に立ちます。また、値丸めを即座に行うため、例えば以下のように、入力値に値丸めを行わないために合計値にゼロの加算を追加するだけで結果が変わってしまうといった、現在の精度よりも細かい値の影響が紛れ込む問題を防げるという恩恵もあります。以下の例は、丸められていない入力を使うということは和にゼロを加えると結果が変わり得るという見本です:

>>> getcontext().prec = 3
>>> Decimal('3.4445') + Decimal('1.0023')
Decimal('4.45')
>>> Decimal('3.4445') + Decimal(0) + Decimal('1.0023')
Decimal('4.44')

This method implements the to-number operation of the IBM specification. If the argument is a string, no leading or trailing whitespace is permitted.

create_decimal_from_float(f)

浮動小数点数 f から新しい Decimal インスタンスを生成しますが、 self をコンテキストとして丸めます。 Decimal.from_float() クラスメソッドとは違い、変換にコンテキストの精度、丸めメソッド、フラグ、そしてトラップが適用されます。

>>> context = Context(prec=5, rounding=ROUND_DOWN)
>>> context.create_decimal_from_float(math.pi)
Decimal('3.1415')
>>> context = Context(prec=5, traps=[Inexact])
>>> context.create_decimal_from_float(math.pi)
Traceback (most recent call last):
    ...
decimal.Inexact: None

バージョン 3.1 で追加.

Etiny()

Emin - prec + 1 に等しい値を返します。演算結果の劣化が起こる桁の最小値です。アンダーフローが起きた場合、指数は Etiny に設定されます。

Etop()

Emax - prec + 1 に等しい値を返します。

Decimal を使った処理を行う場合、通常は Decimal インスタンスを生成して、算術演算を適用するというアプローチをとります。演算はアクティブなスレッドにおける現在のコンテキストの下で行われます。もう一つのアプローチは、コンテキストのメソッドを使った特定のコンテキスト下での計算です。コンテキストのメソッドは Decimal クラスのメソッドに似ているので、ここでは簡単な説明にとどめます。

abs(x)

x の絶対値を返します。

add(x, y)

xy の和を返します。

canonical(x)

同じ Decimal オブジェクト x を返します。

compare(x, y)

xy を数値として比較します。

compare_signal(x, y)

二つの演算対象の値を数値として比較します。

compare_total(x, y)

二つの演算対象を抽象的な表現を使って比較します。

compare_total_mag(x, y)

二つの演算対象を抽象的な表現を使い符号を無視して比較します。

copy_abs(x)

x のコピーの符号を 0 にセットして返します。

copy_negate(x)

x のコピーの符号を反転して返します。

copy_sign(x, y)

y から x に符号をコピーします。

divide(x, y)

xy で除算した値を返します。

divide_int(x, y)

xy で除算した値を整数に切り捨てて返します。

divmod(x, y)

二つの数値間の除算を行い、結果の整数部を返します。

exp(x)

e ** x を返します。

fma(x, y, z)

xy 倍したものに z を加えて返します。

is_canonical(x)

x が標準的(canonical)ならば True を返します。そうでなければ False です。

is_finite(x)

x が有限ならば True を返します。そうでなければ False です。

is_infinite(x)

x が無限ならば True を返します。そうでなければ False です。

is_nan(x)

x が qNaN か sNaN であれば True を返します。そうでなければ False です。

is_normal(x)

x が通常の数ならば True を返します。そうでなければ False です。

is_qnan(x)

x が無言 NaN であれば True を返します。そうでなければ False です。

is_signed(x)

x が負の数であれば True を返します。そうでなければ False です。

is_snan(x)

x が発信 NaN であれば True を返します。そうでなければ False です。

is_subnormal(x)

x が非正規数であれば True を返します。そうでなければ False です。

is_zero(x)

x がゼロであれば True を返します。そうでなければ False です。

ln(x)

x の自然対数(底 e の対数)を返します。

log10(x)

x の底 10 の対数を返します。

logb(x)

演算対象の MSD の大きさの指数部を返します。

logical_and(x, y)

それぞれの桁に論理演算 and を当てはめます。

logical_invert(x)

x の全ての桁を反転させます。

logical_or(x, y)

それぞれの桁に論理演算 or を当てはめます。

logical_xor(x, y)

それぞれの桁に論理演算 xor を当てはめます。

max(x, y)

二つの値を数値として比較し、大きいほうを返します。

max_mag(x, y)

値を符号を無視して数値として比較します。

min(x, y)

二つの値を数値として比較し、小さいほうを返します。

min_mag(x, y)

値を符号を無視して数値として比較します。

minus(x)

Python における単項マイナス演算子に対応する演算です。

multiply(x, y)

xy の積を返します。

next_minus(x)

x より小さい最大の表現可能な数を返します。

next_plus(x)

x より大きい最小の表現可能な数を返します。

next_toward(x, y)

xy の方向に向かって最も近い数を返します。

normalize(x)

x をもっとも単純な形にします。

number_class(x)

x のクラスを指し示すものを返します。

plus(x)

Python における単項のプラス演算子に対応する演算です。コンテキストにおける精度や値丸めを適用するので、等値 (identity) 演算とは 違います

power(x, y, modulo=None)

xy 乗を計算します。modulo が指定されていればモジュロを取ります。

引数が 2 つの場合、 x**y を計算します。x が負の場合、 y は整数でなければなりません。y が整数、結果が有限、結果が 'precision' 桁で正確に表現できる、という条件をすべて満たさない場合、結果は不正確になります。結果はコンテキストの丸めモードを使って丸められます。結果は常に、Python バージョンにおいて正しく丸められます。

バージョン 3.3 で変更: C モジュールは power() を適切に丸められた exp() および ln() 関数によって計算します。結果は well-defined ですが、「ほとんどの場合には適切に丸められる」だけです。

引数が 3 つの場合、 (x**y) % modulo を計算します。この 3 引数の形式の場合、引数には以下の制限が課せられます。

  • 全ての引数は整数
  • y は非負でなければならない
  • xy の少なくともどちらかはゼロでない
  • modulo は非零で大きくても 'precision' 桁

Context.power(x, y, modulo) で得られる値は (x**y) % modulo を精度無制限で計算して得られるものと同じ値ですが、より効率的に計算されます。結果の指数は x, y, modulo の指数に関係なくゼロです。この計算は常に正確です。

quantize(x, y)

x に値丸めを適用し、指数を y にした値を返します。

radix()

単に 10 を返します。何せ十進ですから :)

remainder(x, y)

整数除算の剰余を返します。

剰余がゼロでない場合、符号は割られる数の符号と同じになります。

remainder_near(x, y)

x - y * n を返します。ここで nx / y の正確な値に一番近い整数です (この結果が 0 ならばその符号は x の符号と同じです)。

rotate(x, y)

xy 回巡回したコピーを返します。

same_quantum(x, y)

2つの演算対象が同じ指数を持っている場合に True を返します。

scaleb(x, y)

一つめの演算対象の指数部に二つめの値を加えたものを返します。

shift(x, y)

xy 回シフトしたコピーを返します。

sqrt(x)

x の平方根を精度いっぱいまで求めます。

subtract(x, y)

xy の間の差を返します。

to_eng_string(x)

文字列に変換します。指数が必要なら工学表記が使われます。

工学表記法では指数は 3 の倍数になります。これにより、基数の小数部には最大で 3 桁までの数字が残されるとともに、末尾に 1 つまたは 2 つの 0 の付加が必要とされるかもしれません。

to_integral_exact(x)

最近傍の整数に値を丸めます。

to_sci_string(x)

数値を科学表記で文字列に変換します。

9.4.4. 定数

この節の定数は C モジュールにのみ意味があります。互換性のために、pure Python 版も含まれます。

  32-bit 64-bit
decimal.MAX_PREC
425000000 999999999999999999
decimal.MAX_EMAX
425000000 999999999999999999
decimal.MIN_EMIN
-425000000 -999999999999999999
decimal.MIN_ETINY
-849999999 -1999999999999999997
decimal.HAVE_THREADS

デフォルト値は True です。Python がスレッド無しでコンパイルされている場合、C 版は自動的にコストがかかるスレッドローカルなコンテキスト機構を使用不可にします。この場合、値は False です。

9.4.5. 丸めモード

decimal.ROUND_CEILING

Infinity 方向に丸めます。

decimal.ROUND_DOWN

ゼロ方向に丸めます。

decimal.ROUND_FLOOR

-Infinity 方向に丸めます。

decimal.ROUND_HALF_DOWN

近い方に、引き分けはゼロ方向に向けて丸めます。

decimal.ROUND_HALF_EVEN

近い方に、引き分けは偶数整数方向に向けて丸めます。

decimal.ROUND_HALF_UP

近い方に、引き分けはゼロから遠い方向に向けて丸めます。

decimal.ROUND_UP

ゼロから遠い方向に丸めます。

decimal.ROUND_05UP

ゼロ方向に丸めた後の最後の桁が 0 または 5 ならばゼロから遠い方向に、そうでなければゼロ方向に丸めます。

9.4.6. シグナル

シグナルは、計算中に生じた様々なエラー条件を表現します。各々のシグナルは一つのコンテキストフラグと一つのトラップイネーブラに対応しています。

コンテキストフラグは、該当するエラー条件に遭遇するたびにセットされます。演算後にフラグを調べれば、演算に関する情報 (例えば計算が厳密だったかどうか) がわかります。フラグを調べたら、次の計算を始める前にフラグを全てクリアするようにしてください。

あるコンテキストのトラップイネーブラがあるシグナルに対してセットされている場合、該当するエラー条件が生じると Python の例外を送出します。例えば、 DivisionByZero が設定されていると、エラー条件が生じた際に DivisionByZero 例外を送出します。

class decimal.Clamped

値の表現上の制限に沿わせるために指数部が変更されたことを通知します。

通常、クランプ (clamp) は、指数部がコンテキストにおける指数桁の制限値 Emin および Emax を越えた場合に発生します。可能な場合には、係数部にゼロを加えた表現に合わせて指数部を減らします。

class decimal.DecimalException

他のシグナルの基底クラスで、 ArithmeticError のサブクラスです。

class decimal.DivisionByZero

有限値をゼロで除算したときのシグナルです。

除算やモジュロ除算、数を負の値で累乗した場合に起きることがあります。このシグナルをトラップしない場合、演算結果は Infinity または -Infinity になり、その符号は演算に使った入力に基づいて決まります。

class decimal.Inexact

値の丸めによって演算結果から厳密さが失われたことを通知します。

このシグナルは値丸め操作中にゼロでない桁を無視した際に生じます。演算結果は値丸め後の値です。シグナルのフラグやトラップは、演算結果の厳密さが失われたことを検出するために使えるだけです。

class decimal.InvalidOperation

無効な演算が実行されたことを通知します。

ユーザが有意な演算結果にならないような操作を要求したことを示します。このシグナルをトラップしない場合、 NaN を返します。このシグナルの発生原因として考えられるのは、以下のような状況です:

Infinity - Infinity
0 * Infinity
Infinity / Infinity
x % 0
Infinity % x
sqrt(-x) and x > 0
0 ** 0
x ** (non-integer)
x ** Infinity
class decimal.Overflow

数値オーバフローを示すシグナルです。

このシグナルは、値丸めを行った後の指数部が Emax より大きいことを示します。シグナルをトラップしない場合、演算結果は値丸めのモードにより、表現可能な最大の数値になるように内側へ引き込んで丸めを行った値か、 Infinity になるように外側に丸めた値のいずれかになります。いずれの場合も、 Inexact および Rounded が同時にシグナルされます。

class decimal.Rounded

情報が全く失われていない場合も含み、値丸めが起きたときのシグナルです。

このシグナルは、値丸めによって桁がなくなると常に発生します。なくなった桁がゼロ (例えば 5.00 を丸めて 5.0 になった場合) であってもです。このシグナルをトラップしなければ、演算結果をそのまま返します。このシグナルは有効桁数の減少を検出する際に使います。

class decimal.Subnormal

値丸めを行う前に指数部が Emin より小さかったことを示すシグナルです。

演算結果が微小である場合 (指数が小さすぎる場合) に発生します。このシグナルをトラップしなければ、演算結果をそのまま返します。

class decimal.Underflow

演算結果が値丸めによってゼロになった場合に生じる数値アンダフローです。

演算結果が微小なため、値丸めによってゼロになった場合に発生します。 Inexact および Subnormal シグナルも同時に発生します。

class decimal.FloatOperation

float と Decimal の混合の厳密なセマンティクスを有効にします。

シグナルがトラップされなかった場合 (デフォルト)、Decimal コンストラクタ、 create_decimal() 、およびすべての比較演算子において float と Decimal の混合が許されます。変換も比較も正確です。コンテキストフラグ内に FloatOperation を設定することで、混合操作は現れるたびに暗黙に記録されます。 from_float()create_decimal_from_float() による明示的な変換はフラグを設定しません。

そうでなければ (シグナルがトラップされれば)、等価性比較および明示的な変換のみが静かにに行われ、その他の混合演算は FloatOperation を送出します。

これらのシグナルの階層構造をまとめると、以下の表のようになります:

exceptions.ArithmeticError(exceptions.Exception)
    DecimalException
        Clamped
        DivisionByZero(DecimalException, exceptions.ZeroDivisionError)
        Inexact
            Overflow(Inexact, Rounded)
            Underflow(Inexact, Rounded, Subnormal)
        InvalidOperation
        Rounded
        Subnormal
        FloatOperation(DecimalException, exceptions.TypeError)

9.4.7. 浮動小数点数に関する注意

9.4.7.1. 精度を上げて丸め誤差を抑制する

十進浮動小数点数を使うと、十進数表現による誤差を抑制できます (0.1 を正確に表現できるようになります); しかし、ゼロでない桁が一定の精度を越えている場合には、演算によっては依然として値丸めによる誤差を引き起こします。

値丸めによる誤差の影響は、桁落ちを生じるような、ほとんど相殺される量での加算や減算によって増幅されます。Knuth は、十分でない計算精度の下で値丸めを伴う浮動小数点演算を行った結果、加算の結合則や分配則における恒等性が崩れてしまう例を二つ示しています:

# Examples from Seminumerical Algorithms, Section 4.2.2.
>>> from decimal import Decimal, getcontext
>>> 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')

9.4.7.2. 特殊値

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 は、無効な演算結果に対して特別な処理を行うために計算を停止する必要がある場合に便利です。

Python の比較演算は NaN が関わってくると少し驚くようなことがあります。等価性のテストの一方の対象が無言または発信 NaN である場合いつでも False を返し(たとえ Decimal('NaN')==Decimal('NaN') でも)、一方で不等価をテストするといつでも True を返します。二つの Decimal を <, <=, > または >= を使って比較する試みは一方が NaN である場合には InvalidOperation シグナルを送出し、このシグナルをトラップしなければ結果は False に終わります。汎用十進演算仕様は直接の比較の振る舞いについて定めていないことに注意しておきましょう。ここでの NaN が関係する比較ルールは IEEE 854 標準から持ってきました (section 5.7 の Table 3 を見て下さい)。厳格に標準遵守を貫くなら、 compare() および compare-signal() メソッドを代わりに使いましょう。

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

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

>>> 1 / Decimal('Infinity')
Decimal('0E-1000026')

9.4.8. スレッドを使った処理

関数 getcontext() は、スレッド毎に別々の Context オブジェクトにアクセスします。別のスレッドコンテキストを持つということは、複数のスレッドが互いに影響を及ぼさずに (getcontext().prec=10 のような) 変更を適用できるということです。

同様に、setcontext() 関数は自動的に引数のコンテキストを現在のスレッドのコンテキストに設定します。

getcontext() を呼び出す前に setcontext() が呼び出されていなければ、現在のスレッドで使うための新たなコンテキストを生成するために getcontext() が自動的に呼び出されます。

新たなコンテキストは、DefaultContext と呼ばれる雛形からコピーされます。アプリケーションを通じて全てのスレッドに同じ値を使うようにデフォルトを設定したければ、DefaultContext オブジェクトを直接変更します。 getcontext() を呼び出すスレッド間で競合条件が生じないようにするため、DefaultContext への変更はいかなるスレッドを開始するよりも 前に 行わなければなりません。以下に例を示します:

# Set applicationwide defaults for all threads about to be launched
DefaultContext.prec = 12
DefaultContext.rounding = ROUND_DOWN
DefaultContext.traps = ExtendedContext.traps.copy()
DefaultContext.traps[InvalidOperation] = 1
setcontext(DefaultContext)

# Afterwards, the threads can be started
t1.start()
t2.start()
t3.start()
 . . .

9.4.9. レシピ

Decimal クラスの利用を実演している例をいくつか示します。これらはユーティリティ関数としても利用できます:

def moneyfmt(value, places=2, curr='', sep=',', dp='.',
             pos='', neg='-', trailneg=''):
    """Convert Decimal to a money formatted string.

    places:  required number of places after the decimal point
    curr:    optional currency symbol before the sign (may be blank)
    sep:     optional grouping separator (comma, period, space, or blank)
    dp:      decimal point indicator (comma or period)
             only specify as blank when places is zero
    pos:     optional sign for positive numbers: '+', space or blank
    neg:     optional sign for negative numbers: '-', '(', space or blank
    trailneg:optional trailing minus indicator:  '-', ')', space or blank

    >>> d = Decimal('-1234567.8901')
    >>> moneyfmt(d, curr='$')
    '-$1,234,567.89'
    >>> moneyfmt(d, places=0, sep='.', dp='', neg='', trailneg='-')
    '1.234.568-'
    >>> moneyfmt(d, curr='$', neg='(', trailneg=')')
    '($1,234,567.89)'
    >>> moneyfmt(Decimal(123456789), sep=' ')
    '123 456 789.00'
    >>> moneyfmt(Decimal('-0.02'), neg='<', trailneg='>')
    '<0.02>'

    """
    q = Decimal(10) ** -places      # 2 places --> '0.01'
    sign, digits, exp = value.quantize(q).as_tuple()
    result = []
    digits = list(map(str, digits))
    build, next = result.append, digits.pop
    if sign:
        build(trailneg)
    for i in range(places):
        build(next() if digits else '0')
    if places:
        build(dp)
    if not digits:
        build('0')
    i = 0
    while digits:
        build(next())
        i += 1
        if i == 3 and digits:
            i = 0
            build(sep)
    build(curr)
    build(neg if sign else pos)
    return ''.join(reversed(result))

def pi():
    """Compute Pi to the current precision.

    >>> print(pi())
    3.141592653589793238462643383

    """
    getcontext().prec += 2  # extra digits for intermediate steps
    three = Decimal(3)      # substitute "three=3.0" for regular floats
    lasts, t, s, n, na, d, da = 0, three, 3, 1, 0, 0, 24
    while s != lasts:
        lasts = s
        n, na = n+na, na+8
        d, da = d+da, da+32
        t = (t * n) / d
        s += t
    getcontext().prec -= 2
    return +s               # unary plus applies the new precision

def exp(x):
    """Return e raised to the power of x.  Result type matches input type.

    >>> print(exp(Decimal(1)))
    2.718281828459045235360287471
    >>> print(exp(Decimal(2)))
    7.389056098930650227230427461
    >>> print(exp(2.0))
    7.38905609893
    >>> print(exp(2+0j))
    (7.38905609893+0j)

    """
    getcontext().prec += 2
    i, lasts, s, fact, num = 0, 0, 1, 1, 1
    while s != lasts:
        lasts = s
        i += 1
        fact *= i
        num *= x
        s += num / fact
    getcontext().prec -= 2
    return +s

def cos(x):
    """Return the cosine of x as measured in radians.

    The Taylor series approximation works best for a small value of x.
    For larger values, first compute x = x % (2 * pi).

    >>> print(cos(Decimal('0.5')))
    0.8775825618903727161162815826
    >>> print(cos(0.5))
    0.87758256189
    >>> print(cos(0.5+0j))
    (0.87758256189+0j)

    """
    getcontext().prec += 2
    i, lasts, s, fact, num, sign = 0, 0, 1, 1, 1, 1
    while s != lasts:
        lasts = s
        i += 2
        fact *= i * (i-1)
        num *= x * x
        sign *= -1
        s += num / fact * sign
    getcontext().prec -= 2
    return +s

def sin(x):
    """Return the sine of x as measured in radians.

    The Taylor series approximation works best for a small value of x.
    For larger values, first compute x = x % (2 * pi).

    >>> print(sin(Decimal('0.5')))
    0.4794255386042030002732879352
    >>> print(sin(0.5))
    0.479425538604
    >>> print(sin(0.5+0j))
    (0.479425538604+0j)

    """
    getcontext().prec += 2
    i, lasts, s, fact, num, sign = 1, 0, x, 1, x, 1
    while s != lasts:
        lasts = s
        i += 2
        fact *= i * (i-1)
        num *= x * x
        sign *= -1
        s += num / fact * sign
    getcontext().prec -= 2
    return +s

9.4.10. Decimal FAQ

Q. decimal.Decimal('1234.5') などと打ち込むのは煩わしいのですが、対話式インタプリタを使う際にタイプ量を少なくする方法はありませんか?

A. コンストラクタを1文字に縮める人もいるようです:

>>> D = decimal.Decimal
>>> D('1.23') + D('3.45')
Decimal('4.68')

Q. 小数点以下2桁の固定小数点数のアプリケーションの中で、いくつかの入力が余計な桁を保持しているのでこれを丸めなければなりません。その他のものに余計な桁はなくそのまま使えます。どのメソッドを使うのがいいでしょうか?

A. quantize() メソッドで固定した桁に丸められます。 Inexact トラップを設定しておけば、確認にも有用です:

>>> TWOPLACES = Decimal(10) ** -2       # same as Decimal('0.01')
>>> # Round to two places
>>> Decimal('3.214').quantize(TWOPLACES)
Decimal('3.21')
>>> # Validate that a number does not exceed two places
>>> Decimal('3.21').quantize(TWOPLACES, context=Context(traps=[Inexact]))
Decimal('3.21')
>>> Decimal('3.214').quantize(TWOPLACES, context=Context(traps=[Inexact]))
Traceback (most recent call last):
   ...
Inexact: None

Q. 正当な2桁の入力が得られたとして、その正当性をアプリケーション実行中も変わらず保ち続けるにはどうすればいいでしょうか?

A. 加減算あるいは整数との乗算のような演算は自動的に固定小数点を守ります。その他の除算や整数以外の乗算などは小数点以下の桁を変えてしまいますので実行後は quantize() ステップが必要です:

>>> a = Decimal('102.72')           # Initial fixed-point values
>>> b = Decimal('3.17')
>>> a + b                           # Addition preserves fixed-point
Decimal('105.89')
>>> a - b
Decimal('99.55')
>>> a * 42                          # So does integer multiplication
Decimal('4314.24')
>>> (a * b).quantize(TWOPLACES)     # Must quantize non-integer multiplication
Decimal('325.62')
>>> (b / a).quantize(TWOPLACES)     # And quantize division
Decimal('0.03')

固定小数点のアプリケーションを開発する際は、 quantize() の段階を扱う関数を定義しておくと便利です:

>>> def mul(x, y, fp=TWOPLACES):
...     return (x * y).quantize(fp)
>>> def div(x, y, fp=TWOPLACES):
...     return (x / y).quantize(fp)
>>> mul(a, b)                       # Automatically preserve fixed-point
Decimal('325.62')
>>> div(b, a)
Decimal('0.03')

Q. 一つの値に対して多くの表現方法があります。 200200.0002E202E+4 は全て同じ値で違った精度の数です。これらをただ一つの正規化された値に変換することはできますか?

A. normalize() メソッドは全ての等しい値をただ一つの表現に直します:

>>> values = map(Decimal, '200 200.000 2E2 .02E+4'.split())
>>> [v.normalize() for v in values]
[Decimal('2E+2'), Decimal('2E+2'), Decimal('2E+2'), Decimal('2E+2')]

Q. ある種の十進数値はいつも指数表記で表示されます。指数表記以外の表示にする方法はありますか?

A. 値によっては、指数表記だけが有効桁数を表せる表記法なのです。たとえば、 5.0E+35000 と表してしまうと、値は変わりませんが元々の2桁という有効数字が反映されません。

もしアプリケーションが有効数字の追跡を等閑視するならば、指数部や末尾のゼロを取り除き、有効数字を忘れ、しかし値を変えずにおくことは容易です:

>>> def remove_exponent(d):
...     return d.quantize(Decimal(1)) if d == d.to_integral() else d.normalize()
>>> remove_exponent(Decimal('5E+3'))
Decimal('5000')

Q. 普通の float を Decimal に変換できますか?

A. はい。どんな 2 進浮動小数点数も Decimal として正確に表現できます。ただし、正確な変換は直感的に考えたよりも多い桁になることがあります:

>>> Decimal(math.pi)
Decimal('3.141592653589793115997963468544185161590576171875')

Q. 複雑な計算の中で、精度不足や丸めの異常で間違った結果になっていないことをどうやって保証すれば良いでしょうか。

A. decimal モジュールでは検算は容易です。一番良い方法は、大きめの精度や様々な丸めモードで再計算してみることです。大きく異なった結果が出てきたら、精度不足や丸めの問題や悪条件の入力、または数値計算的に不安定なアルゴリズムを示唆しています。

Q. コンテキストの精度は計算結果には適用されていますが入力には適用されていないようです。様々に異なる精度の入力値を混ぜて計算する時に注意すべきことはありますか?

A. はい。原則として入力値は正確であると見做しておりそれらの値を使った計算も同様です。結果だけが丸められます。入力の強みは "what you type is what you get" (打ち込んだ値が得られる値)という点にあります。入力が丸められないということを忘れていると結果が奇妙に見えるというのは弱点です:

>>> getcontext().prec = 3
>>> Decimal('3.104') + Decimal('2.104')
Decimal('5.21')
>>> Decimal('3.104') + Decimal('0.000') + Decimal('2.104')
Decimal('5.20')

解決策は、精度を増やすか、単項プラス演算子を使って入力の丸めを強制することです:

>>> getcontext().prec = 3
>>> +Decimal('1.23456789')      # unary plus triggers rounding
Decimal('1.23')

もしくは、入力を Context.create_decimal() を使って生成時に丸めてしまうこともできます:

>>> Context(prec=5, rounding=ROUND_DOWN).create_decimal('1.2345678')
Decimal('1.2345')