9.4. decimal --- 10進固定及び浮動小数点数の算術演算¶
バージョン 2.4 で追加.
decimal モジュールは10進の浮動小数点算術をサポートします。 decimal には、 float データ型に比べて、以下のような利点があります:
Decimal 「は人々を念頭にデザインされた浮動小数点モデルを元にしており、必然的に最も重要な指針があります -- コンピュータは人々が学校で習った算術と同じように動作する算術を提供しなければならない」 -- 10進数演算仕様より。
10進数を正確に表現できます。
1.1や2.2のような数は、2 進数の浮動小数点型では正しく表現できません。エンドユーザは普通、 2 進数における1.1 + 2.2の近似値が3.3000000000000003だからといって、そのように表示してほしいとは考えないものです。値の正確さは算術にも及びます。10進の浮動小数点による計算では、
0.1 + 0.1 + 0.1 - 0.3は厳密にゼロに等しくなります。 2 進浮動小数点では5.5511151231257827e-017になってしまいます。ゼロに近い値とはいえ、この誤差は数値間の等価性テストの信頼性を阻害します。また、誤差が蓄積されることもあります。こうした理由から、数値間の等価性を厳しく保たなければならないようなアプリケーションを考えるなら、10進数による数値表現が望ましいということになります。decimalモジュールでは、有効桁数の表記が取り入れられており、例えば1.30 + 1.20は2.50になります。すなわち、末尾のゼロは有効数字を示すために残されます。こうした仕様は通貨計算を行うアプリケーションでは慣例です。乗算の場合、「教科書的な」アプローチでは、乗算の被演算子すべての桁数を使います。例えば、1.3 * 1.2は1.56になり、1.30 * 1.20は1.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')
2進と10進の浮動小数点は、いずれも広く公開されている標準仕様のもとに実装されています。組み込みの浮動小数点型では、標準仕様で提唱されている機能のほんのささやかな部分を利用できるにすぎませんが、
decimalでは標準仕様が要求している全ての機能を利用できます。必要に応じて、プログラマは値の丸めやシグナル処理を完全に制御できます。この中には全ての不正確な操作を例外でブロックして正確な算術を遵守させるオプションもあります。decimal モジュールは「偏見なく、正確な丸めなしの十進算術(固定小数点算術と呼ばれることもある)と丸めありの浮動小数点数算術」(10進数演算仕様より引用)をサポートするようにデザインされました。
このモジュールは、10進数型、算術コンテキスト (context for arithmetic)、そしてシグナル (signal) という三つの概念を中心に設計されています。
10進数型は変更不可能な型です。これは符号、係数部、そして指数を持ちます。有効桁数を残すために、仮数部の末尾にあるゼロは切り詰められません。 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 といったシグナルがあります。
各シグナルには、フラグとトラップイネーブラがあります。演算上何らかのシグナルに遭遇すると、フラグは 1 にセットされます。このとき、もしトラップイネーブラが 1 にセットされていれば、例外を送出します。フラグの値は膠着型 (sticky) なので、演算によるフラグの変化をモニタしたければ、予めフラグをリセットしておかなければなりません。
参考
IBM による汎用10進演算仕様、 The General Decimal Arithmetic Specification 。
9.4.1. クイックスタートチュートリアル¶
普通、 decimal を使うときには、モジュールを import し、現在の演算コンテキストを getcontext() で調べ、必要なら、精度、丸め、有効なトラップを設定します:
>>> from decimal import *
>>> getcontext()
Context(prec=28, rounding=ROUND_HALF_EVEN, Emin=-999999999, Emax=999999999,
capitals=1, 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.41421356237')
>>> Decimal(2) ** Decimal('0.5')
Decimal('1.414213562373095048801688724')
>>> Decimal('NaN')
Decimal('NaN')
>>> Decimal('-Infinity')
Decimal('-Infinity')
新たな 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')
decimal はほとんどの場面で Python の他の機能とうまくやりとりできます。decimal 浮動小数点小劇場 (flying circus) をお見せしましょう:
>>> data = 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) # round() first converts to binary floating point
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=-999999999, Emax=999999999,
capitals=1, 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=-999999999, Emax=999999999,
capitals=1, flags=[Rounded, Inexact], 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[, context]])¶ value に基づいて新たな
Decimalオブジェクトを構築します。value は整数、文字列、タプル、
floatおよび他のDecimalオブジェクトにできます。 value を指定しない場合、Decimal('0')を返します。 value が文字列の場合、先頭と末尾の空白を取り除いた後には以下の 10進数文字列の文法に従わなければなりません: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
value を Unicode 文字列にした場合、他の Unicode 数字も上の
digitの場所に使うことができます。つまり各書記体系における (アラビア-インド系やデーヴァナーガリーなど) の数字や、全角数字0 (u'\uff10') から9 (u'\uff19') までなどです。value を
tupleにする場合、タプルは三つの要素を持ち、それぞれ符号 (正なら0、負なら1)、仮数部を表す数字のtuple、そして指数を表す整数でなければなりません。例えば、Decimal((0, (1, 4, 1, 4), -3))はDecimal('1.414')を返します。value を
floatにする場合、2進浮動小数点数値が損失なく正確に等価な Decimal に変換されます。この変換はしばしば 53 桁以上の精度を要求します。例えば、Decimal(float('1.1'))はDecimal('1.100000000000000088817841970012523233890533447265625')に変換されます。context の精度 (precision) は、記憶される桁数には影響しません。桁数は value に指定した桁数だけから決定されます。例えば、演算コンテキストに指定された精度が 3 桁しかなくても、
Decimal('3.00000')は 5 つのゼロを全て記憶します。context 引数の目的は、 value が正しくない形式の文字列であった場合に行う処理を決めることにあります; 演算コンテキストが
InvalidOperationをトラップするようになっていれば、例外を送出します。それ以外の場合には、コンストラクタは値がNaNのDecimalを返します。一度生成すると、
Decimalオブジェクトは変更不能 (immutable) になります。バージョン 2.6 で変更: 文字列から Decimal インスタンスを生成する際に先頭と末尾の空白が許されることになりました。
バージョン 2.7 で変更: コンストラクタに対する引数に
floatインスタンスも許されるようになりました。10進浮動小数点オブジェクトは、
floatやintのような他の組み込み型と多くの点で似ています。通常の数学演算や特殊メソッドを適用できます。また、Decimalオブジェクトはコピーでき、pickle 化でき、print で出力でき、辞書のキーにでき、集合の要素にでき、比較、保存、他の型 (floatやlong) への型強制を行えます。10進オブジェクトの算術演算と整数や浮動小数点数の算術演算には少々違いがあります。10進オブジェクトに対して剰余演算を適用すると、計算結果の符号は除数の符号ではなく 被除数 の符号と一致します:
>>> (-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 オブジェクトは一般に、算術演算で浮動小数点数と組み合わせることができません。例えば、
Decimalにfloatを足そうとすると、TypeErrorが送出されます。ただしこの規則には例外があります。floatインスタンスxとDecimalインスタンスyを比較する比較演算子です。この例外がなかったとすると、Decimalとfloatインスタンスの比較は、リファレンスマニュアルの 式 (expression) 節で記述されている、異なる型のオブジェクトを比較するときの一般の規則に従うことになり、紛らわしい結果につながります。バージョン 2.7 で変更:
floatインスタンスxとDecimalインスタンスyの比較は、xとyの値に基づく結果を返すようになりました。以前のバージョンでは、どんなfloatインスタンスxとどんなDecimalインスタンスyに対しても、x < yは同じ (任意の) 結果を返していました。こうした標準的な数値型の特性の他に、10進浮動小数点オブジェクトには様々な特殊メソッドがあります:
-
adjusted()¶ 仮数の先頭の一桁だけが残るように右側の数字を追い出す桁シフトを行い、その結果の指数部を返します:
Decimal('321e+5').adjusted()は 7 を返します。最上桁の小数点からの相対位置を調べる際に使います。
-
as_tuple()¶ 数値を表現するための名前付きタプル(named tuple):
DecimalTuple(sign, digittuple, exponent)を返します。バージョン 2.6 で変更: 名前付きタプル (named tuple) を使うようになりました。
-
canonical()¶ 引数の標準的(canonical)エンコーディングを返します。現在のところ、
Decimalインスタンスのエンコーディングは常に標準的なので、この操作は引数に手を加えずに返します。バージョン 2.6 で追加.
-
compare(other[, context])¶ 二つの Decimal インスタンスの値を比較します。この演算は普通の比較メソッド
__cmp__()と同じように振舞いますが、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])¶ この演算は
compare()とほとんど同じですが、全ての NaN がシグナルを送るところが異なります。すなわち、どちらの比較対象も発信 (signaling) NaN でないならば無言 (quiet) NaN である比較対象があたかも発信 NaN であるかのように扱われます。バージョン 2.6 で追加.
-
compare_total(other)¶ 二つの対象を数値によらず抽象表現によって比較します。
compare()に似ていますが、結果はDecimalに全順序を与えます。この順序づけによると、数値的に等しくても異なった表現を持つ二つのDecimalインスタンスの比較は等しくなりません:>>> Decimal('12.0').compare_total(Decimal('12')) Decimal('-1')
無言 NaN と発信 NaN もこの全順序に位置付けられます。この関数の結果は、もし比較対象が同じ表現を持つならば
Decimal('0')であり、一つめの比較対象が二つめより下位にあればDecimal('-1')、上位にあればDecimal('1')です。全順序の詳細については仕様を参照してください。バージョン 2.6 で追加.
-
compare_total_mag(other)¶ 二つの対象を
compare_total()のように数値によらず抽象表現によって比較しますが、両者の符号を無視します。x.compare_total_mag(y)はx.copy_abs().compare_total(y.copy_abs())と等価です。バージョン 2.6 で追加.
-
conjugate()¶ self を返すだけです。このメソッドは十進演算仕様に適合するためだけのものです。
バージョン 2.6 で追加.
-
copy_abs()¶ 引数の絶対値を返します。この演算はコンテキストに影響されず、静かです。すなわち、フラグは変更されず、丸めは行われません。
バージョン 2.6 で追加.
-
copy_negate()¶ 引数の符号を変えて返します。この演算はコンテキストに影響されず、静かです。すなわち、フラグは変更されず、丸めは行われません。
バージョン 2.6 で追加.
-
copy_sign(other)¶ 最初の演算対象のコピーに二つめと同じ符号を付けて返します。たとえば:
>>> Decimal('2.3').copy_sign(Decimal('-1.5')) Decimal('-2.3')
この演算はコンテキストに影響されず、静かです。すなわち、フラグは変更されず、丸めは行われません。
バージョン 2.6 で追加.
-
exp([context])¶ 与えられた数での(自然)指数関数
e**xの値を返します。結果はROUND_HALF_EVEN丸めモードで正しく丸められます。>>> Decimal(1).exp() Decimal('2.718281828459045235360287471') >>> Decimal(321).exp() Decimal('2.561702493119680037517373933E+139')
バージョン 2.6 で追加.
-
from_float(f)¶ 浮動小数点数を正確に小数に変換するクラスメソッドです。
なお、Decimal.from_float(0.1) は Decimal('0.1') と同じではありません。0.1 は二進浮動小数点数で正確に表せないので、その値は表現できる最も近い値、0x1.999999999999ap-4 として記憶されます。浮動小数点数での等価な値は 0.1000000000000000055511151231257827021181583404541015625 です。
>>> 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')
バージョン 2.7 で追加.
-
fma(other, third[, context])¶ 融合積和(fused multiply-add)です。self*other+third を途中結果の積 self*other で丸めを行わずに計算して返します。
>>> Decimal(2).fma(3, 5) Decimal('11')
バージョン 2.6 で追加.
-
is_canonical()¶ 引数が標準的(canonical)ならば
Trueを返し、そうでなければFalseを返します。現在のところ、Decimalのインスタンスは常に標準的なのでこのメソッドの結果はいつでもTrueです。バージョン 2.6 で追加.
-
is_normal()¶ 引数が 正規(normal) のゼロでない有限数値で調整された指数が Emin 以上ならば
Trueを返します。引数がゼロ、非正規(subnormal)、無限大または NaN であればFalseを返します。ここでの 正規 という用語は標準的な(canonical)値を作り出すために使われるnormalize()メソッドにおける意味合いとは異なりますので注意して下さい。バージョン 2.6 で追加.
-
is_subnormal()¶ 引数が非正規数(subnormal)であれば
Trueを、そうでなければFalseを返します。非正規な数値とは、ゼロでなく、有限で、調整された指数が Emin 未満のものを指します。バージョン 2.6 で追加.
-
ln([context])¶ 演算対象の自然対数(底 e の対数)を返します。結果は
ROUND_HALF_EVEN丸めモードで正しく丸められます。バージョン 2.6 で追加.
-
log10([context])¶ 演算対象の底 10 の対数を返します。結果は
ROUND_HALF_EVEN丸めモードで正しく丸められます。バージョン 2.6 で追加.
-
logb([context])¶ 非零の数値については、
Decimalインスタンスとして調整された指数を返します。演算対象がゼロだった場合、Decimal('-Infinity')が返されDivisionByZeroフラグが送出されます。演算対象が無限大だった場合、Decimal('Infinity')が返されます。バージョン 2.6 で追加.
-
logical_and(other[, context])¶ logical_and()は二つの 論理引数 (論理引数 参照)を取る論理演算です。結果は二つの引数の数字ごとのandです。バージョン 2.6 で追加.
-
logical_invert([context])¶ logical_invert()は論理演算です。結果は引数の数字ごとの反転です。バージョン 2.6 で追加.
-
logical_or(other[, context])¶ logical_or()は二つの 論理引数 (論理引数 参照)を取る論理演算です。結果は二つの引数の数字ごとのorです。バージョン 2.6 で追加.
-
logical_xor(other[, context])¶ logical_xor()は二つの 論理引数 (論理引数 参照)を取る論理演算です。結果は二つの引数の数字ごとの排他的論理和です。バージョン 2.6 で追加.
-
max(other[, context])¶ max(self, other)と同じですが、値を返す前に現在のコンテキストに即した丸め規則を適用します。また、NaNに対して、(コンテキストの設定と、発信か無言どちらのタイプであるかに応じて) シグナルを発行するか無視します。
-
min(other[, context])¶ min(self, other)と同じですが、値を返す前に現在のコンテキストに即した丸め規則を適用します。また、NaNに対して、(コンテキストの設定と、発信か無言どちらのタイプであるかに応じて) シグナルを発行するか無視します。
-
next_minus([context])¶ 与えられたコンテキスト(またはコンテキストが渡されなければ現スレッドのコンテキスト)において表現可能な、操作対象より小さい最大の数を返します。
バージョン 2.6 で追加.
-
next_plus([context])¶ 与えられたコンテキスト(またはコンテキストが渡されなければ現スレッドのコンテキスト)において表現可能な、操作対象より大きい最小の数を返します。
バージョン 2.6 で追加.
-
next_toward(other[, context])¶ 二つの比較対象が等しくなければ、一つめの対象に最も近く二つめの対象へ近付く方向の数を返します。もし両者が数値的に等しければ、二つめの対象の符号を採った一つめの対象のコピーを返します。
バージョン 2.6 で追加.
-
normalize([context])¶ 数値を正規化 (normalize) して、右端に連続しているゼロを除去し、
Decimal('0')と同じ結果はすべてDecimal('0e0')に変換します。等価クラスの属性から基準表現を生成する際に用います。たとえば、Decimal('32.100')とDecimal('0.321000e+2')の正規化は、いずれも同じ値Decimal('32.1')になります。
-
number_class([context])¶ 操作対象の クラス を表す文字列を返します。返されるのは以下の10種類のいずれかです。
"-Infinity", 負の無限大であることを示します。"-Normal", 負の通常数であることを示します。"-Subnormal", 負の非正規数であることを示します。"-Zero", 負のゼロであることを示します。"+Zero", 正のゼロであることを示します。"+Subnormal", 正の非正規数であることを示します。"+Normal", 正の通常数であることを示します。"+Infinity", 正の無限大であることを示します。"NaN", 無言 (quiet) NaN (Not a Number) であることを示します。"sNaN", 発信(signaling) NaN であることを示します。
バージョン 2.6 で追加.
-
quantize(exp[, rounding[, context[, watchexp]]])¶ 二つめの操作対象と同じ指数を持つように丸めを行った、一つめの操作対象と等しい値を返します。
>>> Decimal('1.41421356').quantize(Decimal('1.000')) Decimal('1.414')
他の操作と違い、打ち切り(quantize)操作後の係数の長さが精度を越えた場合には、
InvalidOperationがシグナルされます。これによりエラー条件がない限り打ち切られた指数が常に右側の引数と同じになることが保証されます。同様に、他の操作と違い、quantize は Underflow を、たとえ結果が非正規になったり不正確になったとしても、シグナルしません。
二つ目の演算対象の指数が一つ目のそれよりも大きければ丸めが必要かもしれません。この場合、丸めモードは以下のように決められます。
rounding引数が与えられていればそれが使われます。そうでなければcontext引数で決まります。どちらの引数も渡されなければ現在のスレッドのコンテキストの丸めモードが使われます。watchexp が設定されている場合(デフォルト)、処理結果の指数が
Emaxよりも大きい場合やEtinyよりも小さい場合にエラーを返します。
-
remainder_near(other[, context])¶ self を other で割った剰余を返します。これは
self % otherとは違って、剰余の絶対値を小さくするように符号が選ばれます。より詳しく言うと、nをself / 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])¶ 一つめの演算対象の数字を二つめので指定された量だけ巡回(rotate)した結果を返します。二つめの演算対象は -precision から precision までの範囲の整数でなければなりません。この二つめの演算対象の絶対値が何桁ずらすかを決めます。そしてもし正の数ならば巡回の方向は左に、そうでなければ右になります。一つめの演算対象の仮数部は必要ならば精度いっぱいまでゼロで埋められます。符号と指数は変えられません。
バージョン 2.6 で追加.
-
same_quantum(other[, context])¶ self と other が同じ指数を持っているか、あるいは双方とも
NaNである場合に真を返します。
-
scaleb(other[, context])¶ 二つめの演算対象で調整された指数の一つめの演算対象を返します。同じことですが、一つめの演算対象を
10**other倍したものを返します。二つめの演算対象は整数でなければなりません。バージョン 2.6 で追加.
-
shift(other[, context])¶ 一つめの演算対象の数字を二つめので指定された量だけシフトした結果を返します。二つめの演算対象は -precision から precision までの範囲の整数でなければなりません。この二つめの演算対象の絶対値が何桁ずらすかを決めます。そしてもし正の数ならばシフトの方向は左に、そうでなければ右になります。一つめの演算対象の係数は必要ならば精度いっぱいまでゼロで埋められます。符号と指数は変えられません。
バージョン 2.6 で追加.
-
sqrt([context])¶ 平方根を精度いっぱいまで求めます。
-
to_eng_string([context])¶ 文字列に変換します。指数が必要なら工学表記が使われます。
工学表記法では指数は 3 の倍数になります。これにより、基数の小数部には最大で 3 桁までの数字が残されるとともに、末尾に 1 つまたは 2 つの 0 の付加が必要とされるかもしれません。
たとえば、
Decimal('123E+1')はDecimal('1.23E+3')に変換されます。
-
to_integral([rounding[, context]])¶ to_integral_value()メソッドと同じです。to_integralの名前は古いバージョンとの互換性のために残されています。
-
9.4.3. Context オブジェクト¶
コンテキスト (context) とは、算術演算における環境設定です。コンテキストは計算精度を決定し、値丸めの方法を設定し、シグナルのどれが例外になるかを決め、指数の範囲を制限しています。
多重スレッドで処理を行う場合には各スレッドごとに現在のコンテキストがあり、 getcontext() や setcontext() といった関数でアクセスしたり設定変更できます:
-
decimal.getcontext()¶ アクティブなスレッドの現在のコンテキストを返します。
-
decimal.setcontext(c)¶ アクティブなスレッドのコンテキストを c に設定します。
Python 2.5 から、 with 文と localcontext() 関数を使って実行するコンテキストを一時的に変更することもできるようになりました。
-
decimal.localcontext([c])¶ with 文の入口でアクティブなスレッドのコンテキストを c のコピーに設定し、with 文を抜ける時に元のコンテキストに復旧する、コンテキストマネージャを返します。コンテキストが指定されなければ、現在のコンテキストのコピーが使われます。
バージョン 2.5 で追加.
たとえば、以下のコードでは精度を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 with localcontext(BasicContext): # temporarily use the BasicContext print Decimal(1) / Decimal(7) print Decimal(355) / Decimal(113)
新たなコンテキストは、以下で説明する Context コンストラクタを使って生成できます。その他にも、 decimal モジュールでは作成済みのコンテキストを提供しています:
-
class
decimal.BasicContext¶ 汎用10進演算仕様で定義されている標準コンテキストの一つです。精度は 9 桁に設定されています。丸め規則は
ROUND_HALF_UPです。すべての演算結果フラグはクリアされています。Inexact,Rounded,Subnormalを除く全ての演算エラートラップが有効 (例外として扱う) になっています。多くのトラップが有効になっているので、デバッグの際に便利なコンテキストです。
-
class
decimal.ExtendedContext¶ 汎用10進演算仕様で定義されている標準コンテキストの一つです。精度は 9 桁に設定されています。丸め規則は
ROUND_HALF_EVENです。すべての演算結果フラグはクリアされています。トラップは全て無効(演算中に一切例外を送出しない) になっています。トラップが無効になっているので、エラーの伴う演算結果を
NaNやInfinityにし、例外を送出しないようにしたいアプリケーションに向いたコンテキストです。このコンテキストを使うと、他の場合にはプログラムが停止してしまうような状況があっても実行を完了させられます。
-
class
decimal.DefaultContext¶ Contextコンストラクタが新たなコンテキストを作成するさいに雛形にするコンテキストです。このコンテキストのフィールド (精度の設定など) を変更すると、Contextコンストラクタが生成する新たなコンテキストに影響を及ぼします。このコンテキストは、主に多重スレッド環境で便利です。スレッドを開始する前に何らかのフィールドを変更しておくと、システム全体のデフォルト設定に効果を及ぼすことができます。スレッドを開始した後にフィールドを変更すると、競合条件を抑制するためにスレッドを同期化しなければならないので、推奨しません。
単一スレッドの環境では、このコンテキストを使わないよう薦めます。下で述べるように明示的にコンテキストを作成してください。
デフォルトの値は精度 28 桁、丸め規則
ROUND_HALF_EVENで、トラップOverflow,InvalidOperation, およびDivisionByZeroが有効になっています。
上に挙げた三つのコンテキストに加え、 Context コンストラクタを使って新たなコンテキストを生成できます。
-
class
decimal.Context(prec=None, rounding=None, traps=None, flags=None, Emin=None, Emax=None, capitals=1)¶ 新たなコンテキストを生成します。あるフィールドが定義されていないか
Noneであれば、DefaultContextからデフォルト値をコピーします。 flags フィールドが設定されていいかNoneの場合には、全てのフラグがクリアされます。prec フィールドは正の整数で、コンテキストにおける算術演算の計算精度を設定します。
rounding オプションは以下のどれか一つです:
ROUND_CEILING(Infinity寄りの値に丸める),ROUND_DOWN(ゼロ寄りの値に丸める),ROUND_FLOOR(-Infinity寄りの値に丸める),ROUND_HALF_DOWN(近い方に、引き分けはゼロ方向に向けて丸める),ROUND_HALF_EVEN(近い方に、引き分けは偶数整数方向に向けて丸める),ROUND_HALF_UP(近い方に、引き分けはゼロから遠い方向に向けて丸める),ROUND_UP(ゼロから遠い値に丸める), またはROUND_05UP(ゼロ方向に丸めた後の最後の桁が 0 または 5 ならばゼロから遠い方向に、そうでなければゼロ方向に丸める)
traps および flags フィールドには、セットしたいシグナルを列挙します。一般的に、新たなコンテキストを作成するときにはトラップだけを設定し、フラグはクリアしておきます。
Emin および Emax フィールドには、指数の外限を整数で指定します。
capitals フィールドは
0または1(デフォルト) にします。1に設定すると、指数記号を大文字Eで出力します。それ以外の場合にはDecimal('6.02e+23')のようにeを使います。バージョン 2.6 で変更:
ROUND_05UP丸めモードが追加されました。Contextクラスでは、いくつかの汎用のメソッドの他、現在のコンテキストで算術演算を直接行うためのメソッドを数多く定義しています。加えて、Decimalの各メソッドについて (adjusted()およびas_tuple()メソッドを例外として) 対応するContextのメソッドが存在します。たとえば、ContextインスタンスCとDecimalインスタンスxに対して、C.exp(x)はx.exp(context=C)と等価です。それぞれのContextメソッドは、Decimal インスタンスが受け付けられるところならどこでも、Python の整数 (intまたはlongのインスタンス) を受け付けます。-
clear_flags()¶ フラグを全て
0にリセットします。
-
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')
このメソッドは IBM 仕様の to-number 演算を実装したものです。引数が文字列の場合、前や後ろに余計な空白を付けることは許されません。
-
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): ... Inexact: None
バージョン 2.7 で追加.
-
Etop()¶ Emax - prec + 1に等しい値を返します。
Decimalを使った処理を行う場合、通常はDecimalインスタンスを生成して、算術演算を適用するというアプローチをとります。演算はアクティブなスレッドにおける現在のコンテキストの下で行われます。もう一つのアプローチは、コンテキストのメソッドを使った特定のコンテキスト下での計算です。コンテキストのメソッドはDecimalクラスのメソッドに似ているので、ここでは簡単な説明にとどめます。-
abs(x)¶ x の絶対値を返します。
-
add(x, y)¶ x と y の和を返します。
-
canonical(x)¶ 同じ Decimal オブジェクト x を返します。
-
compare(x, y)¶ x と y を数値として比較します。
-
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)¶ x を y で除算した値を返します。
-
divide_int(x, y)¶ x を y で除算した値を整数に切り捨てて返します。
-
divmod(x, y)¶ 二つの数値間の除算を行い、結果の整数部を返します。
-
exp(x)¶ e ** x を返します。
-
fma(x, y, z)¶ x を y 倍したものに 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)¶ x と y の積を返します。
-
next_minus(x)¶ x より小さい最大の表現可能な数を返します。
-
next_plus(x)¶ x より大きい最小の表現可能な数を返します。
-
next_toward(x, y)¶ x に y の方向に向かって最も近い数を返します。
-
normalize(x)¶ x をもっとも単純な形にします。
-
number_class(x)¶ x のクラスを指し示すものを返します。
-
plus(x)¶ Python における単項のプラス演算子に対応する演算です。コンテキストにおける精度や値丸めを適用するので、等値 (identity) 演算とは 違います。
-
power(x, y[, modulo])¶ xのy乗を計算します。moduloが指定されていればモジュロを取ります。二引数であれば
x**yを計算します。xが負であればyは整でなければなりません。結果はyが整数であって結果が有限になり 'precision' 桁で正確に表現できるのでなければ不正確になります。その結果は現スレッドのコンテキストの丸めモードを使って正しく丸められます。三引数であれば
(x**y) % moduloを計算します。この形式の場合、以下の制限が引数に掛かります:全ての引数は整
yは非負でなければならないxとyの少なくともどちらかはゼロでないmoduloは非零で大きくても 'precision' 桁
Context.power(x, y, modulo)で得られる値は(x**y) % moduloを精度無制限で計算して得られるものと同じ値ですが、より効率的に計算されます。結果の指数はx,y,moduloの指数に関係なくゼロです。この計算は常に正確です。バージョン 2.6 で変更:
x**y形式でyが非整数で構わないことになりました。三引数バージョンがより厳格な要求をするようになりました。
-
quantize(x, y)¶ x に値丸めを適用し、指数を y にした値を返します。
-
radix()¶ 単に 10 を返します。何せ十進ですから :)
-
remainder(x, y)¶ 整数除算の剰余を返します。
剰余がゼロでない場合、符号は割られる数の符号と同じになります。
-
remainder_near(x, y)¶ x - y * nを返します。ここで n はx / yの正確な値に一番近い整数です (この結果が 0 ならばその符号は x の符号と同じです)。
-
rotate(x, y)¶ x の y 回巡回したコピーを返します。
-
same_quantum(x, y)¶ 2つの演算対象が同じ指数を持っている場合に
Trueを返します。
-
scaleb(x, y)¶ 一つめの演算対象の指数部に二つめの値を加えたものを返します。
-
shift(x, y)¶ x を y 回シフトしたコピーを返します。
-
sqrt(x)¶ x の平方根を精度いっぱいまで求めます。
-
subtract(x, y)¶ x と y の間の差を返します。
-
to_eng_string(x)¶ 文字列に変換します。指数が必要なら工学表記が使われます。
工学表記法では指数は 3 の倍数になります。これにより、基数の小数部には最大で 3 桁までの数字が残されるとともに、末尾に 1 つまたは 2 つの 0 の付加が必要とされるかもしれません。
-
to_integral_exact(x)¶ 最近傍の整数に値を丸めます。
-
to_sci_string(x)¶ 数値を科学表記で文字列に変換します。
9.4.4. シグナル¶
シグナルは、計算中に生じた様々なエラー条件を表現します。各々のシグナルは一つのコンテキストフラグと一つのトラップイネーブラに対応しています。
コンテキストフラグは、該当するエラー条件に遭遇するたびにセットされます。演算後にフラグを調べれば、演算に関する情報 (例えば計算が厳密だったかどうか) がわかります。フラグを調べたら、次の計算を始める前にフラグを全てクリアするようにしてください。
あるコンテキストのトラップイネーブラがあるシグナルに対してセットされている場合、該当するエラー条件が生じると 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 x._rescale( non-integer ) 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シグナルも同時に発生します。
これらのシグナルの階層構造をまとめると、以下の表のようになります:
exceptions.ArithmeticError(exceptions.StandardError)
DecimalException
Clamped
DivisionByZero(DecimalException, exceptions.ZeroDivisionError)
Inexact
Overflow(Inexact, Rounded)
Underflow(Inexact, Rounded, Subnormal)
InvalidOperation
Rounded
Subnormal
9.4.5. 浮動小数点数に関する注意¶
9.4.5.1. 精度を上げて丸め誤差を抑制する¶
10進浮動小数点数を使うと、10進数表現による誤差を抑制できます (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.5.2. 特殊値¶
decimal モジュールの数体系では、 NaN, sNaN, -Infinity, Infinity, および二つのゼロ、 +0 と -0 といった特殊な値を提供しています。
無限大 (Infinity) は Decimal('Infinity') で直接構築できます。また、 DivisionByZero をトラップせずにゼロで除算を行った場合にも出てきます。同様に、 Overflow シグナルをトラップしなければ、表現可能な最大の数値の制限を越えた値を丸めたときに出てきます。
無限大には符号があり (アフィン: affine であり)、算術演算に使用でき、非常に巨大で不確定の(indeterminate)値として扱われます。例えば、無限大に何らかの定数を加算すると、演算結果は別の無限大になります。
演算によっては結果が不確定になるものがあり、 NaN を返します。ただし、 InvalidOperation シグナルをトラップするようになっていれば例外を送出します。例えば、 0/0 は NaN を返します。 NaN は「非数値 (not a number)」を表します。このような NaN は暗黙のうちに生成され、一度生成されるとそれを他の計算にも流れてゆき、関係する個々の演算全てが個別の NaN を返すようになります。この挙動は、たまに入力値が欠けるような状況で一連の計算を行う際に便利です --- 特定の計算に対しては無効な結果を示すフラグを立てつつ計算を進められるからです。
一方、 NaN の変種である sNaN は関係する全ての演算で演算後にシグナルを送出します。 sNaN は、無効な演算結果に対して特別な処理を行うために計算を停止する必要がある場合に便利です。
Python の比較演算は NaN が関わってくると少し驚くようなことがあります。等価性のテストの一方の対象が無言または発信 NaN である場合いつでも False を返し(たとえ Decimal('NaN')==Decimal('NaN') でも)、一方で不等価をテストするといつでも True を返します。二つの Decimal を <, <=, > または >= を使って比較する試みは一方が NaN である場合には InvalidOperation シグナルを送出し、このシグナルをトラップしなければ結果は False に終わります。汎用10進演算仕様は直接の比較の振る舞いについて定めていないことに注意しておきましょう。ここでの NaN が関係する比較ルールは IEEE 854 標準から持ってきました (section 5.7 の Table 3 を見て下さい)。厳格に標準遵守を貫くなら、 compare() および compare-signal() メソッドを代わりに使いましょう。
アンダフローの起きた計算は、符号付きのゼロ (signed zero) を返すことがあります。符号は、より高い精度で計算を行った結果の符号と同じになります。符号付きゼロの大きさはやはりゼロなので、正のゼロと負のゼロは等しいとみなされ、符号は単なる参考にすぎません。
二つの符号付きゼロが区別されているのに等価であることに加えて、異なる精度におけるゼロの表現はまちまちなのに、値は等価とみなされるということがあります。これに慣れるには多少時間がかかります。正規化浮動小数点表現に目が慣れてしまうと、以下の計算でゼロに等しい値が返っているとは即座に分かりません:
>>> 1 / Decimal('Infinity')
Decimal('0E-1000000026')
9.4.6. スレッドを使った処理¶
関数 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.7. レシピ¶
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 = map(str, digits)
build, next = result.append, digits.pop
if sign:
build(trailneg)
for i in range(places):
build(next() if digits else '0')
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.
>>> 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.
>>> 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.8. 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. 一つの値に対して多くの表現方法があります。 200 と 200.000 と 2E2 と 02E+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. ある種の10進数値はいつも指数表記で表示されます。指数表記以外の表示にする方法はありますか?
A. 値によっては、指数表記だけが有効桁数を表せる表記法なのです。たとえば、 5.0E+3 を 5000 と表してしまうと、値は変わりませんが元々の2桁という有効数字が反映されません。
もしアプリケーションが有効数字の追跡を等閑視するならば、指数部や末尾のゼロを取り除き、有効数字を忘れ、しかし値を変えずにおくことは容易です:
def remove_exponent(d):
'''Remove exponent and trailing zeros.
>>> remove_exponent(Decimal('5E+3'))
Decimal('5000')
'''
return d.quantize(Decimal(1)) if d == d.to_integral() else d.normalize()
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')
