decimal.Decimal
組み込みの浮動小数点数型 (floatクラス) オブジェクトを使った数値計算には非常に厄介な一面があります。たとえば、0.1 + 0.1 + 0.1 - 0.3 というような簡単な演算でさえ正確な値を返してくれません(理由については浮動小数点数の記事を参照してください)。
x = 0.1 + 0.1 + 0.1 - 0.3
print(x)
# 5.551115123125783e-17
誤差は極めて小さなものですが、開発するソフトウェアの種類によっては大きな障壁となる場合があります。こうした問題を回避するために、十進浮動小数点数型 (Decimalクラス) が用意されています。0.1 + 0.1 + 0.1 - 0.3 という計算をDecimalクラスのインスタンスを使って実行してみましょう。
# PYTHON_DECIMAL
# In[1]
# decimalモジュールをインポート
import decimal
# Decimalクラスのインスタンスを作成
x = decimal.Decimal('0.1')
y = decimal.Decimal('0.3')
# xのクラスを表示
print(type(x))
# 0.1 + 0.1 + 0.1 - 0.3を計算
print(x + x + x - y)
# <class 'decimal.Decimal'>
# 0.0
今度は正確に 0.0 という値を返しました。Decimalクラスは十進数を正確に表現できるように設計されています。decimal.Decimal(x) の引数 x に小数点を含む数字をクォーテーションまたはダブルクォーテーションで囲った文字列形式で渡すと、表記された通りの十進数の値 (Decimalクラスのインスタンス) を生成します。すなわち "1.3" は正確に十進数 1.3 を意味する Decimal型数値であり、小数点以下に(たとえば 1.3000001 のような)小さな誤差を伴う近似値ではありません。ただし、数字を囲わずに渡した場合は、メモリにある浮動小数点数をそのまま十進数の値として保存してしまうので注意してください。
# In[2]
# Decimalクラスのインスタンスを作成
val = decimal.Decimal(1.30)
print(val)
# 1.3000000000000000444089209850062616169452667
引数に整数型を指定すると、そのまま Decimal 型に変換されます(もともと整数値は進数変換で誤差を生じません)。
# In[3]
val = decimal.Decimal(1)
print(val)
# 1
Decimal 型数値同士を演算させるとDecimal 型数値が返ります。Decimal 型数値には有効桁数の概念が反映されています。下のサンプルコードにあるように、1.40 + 1.20 は 2.60, 1.40 * 1.20 は 1.6800 というオブジェクトを生成します。除算については、割り切れない場合にはデフォルト設定で 28 桁の数値に丸められます(後述するように Context オブジェクトのデータ属性に値を代入して桁数設定を変えることもできます)。
# In[4]
x = decimal.Decimal('1.40')
y = decimal.Decimal('1.20')
# Decimalオブジェクト同士の演算
print("1.40 + 1.20 :", x + y)
print("1.40 - 1.20 :", x - y)
print("1.40 * 1.20 :", x * y)
print("1.40 / 1.20 :", x / y)
print("1.40 // 1.20 :", x // y)
print("1.40 % 1.20 :", x % y)
# 1.40 + 1.20 : 2.60
# 1.40 - 1.20 : 0.20
# 1.40 * 1.20 : 1.6800
# 1.40 / 1.20 : 1.166666666666666666666666667
# 1.40 // 1.20 : 1
# 1.40 % 1.20 : 0.20
Decimal 型数値と int 型数値を演算させると、Decimal 型数値が返ります。
# In[5]
# Decimal型数値
x = decimal.Decimal('10.5')
# int型数値
y = 5
# Decimal + int
z = x + y
print(z)
print(type(z))
# 15.5
# <class 'decimal.Decimal'>
Decimal 型数値と float 型数値を演算させことはできません (TypeError が発生します)。
decimal.Context
getcontext()関数 を使うと、現在の Decimal 型の演算環境設定 (context) を表示させることができます。
# PYTHON_DECIMAL_CONTEXT
# In[1]
import decimal
# Contextオブジェクトを作成
val = decimal.getcontext()
print(type(val))
print(val)
# <class 'decimal.Context'>
# Context(prec=28, rounding=ROUND_HALF_EVEN, Emin=-999999,
# Emax=999999, capitals=1, clamp=0,
# flags=[DivisionByZero,Inexact, Rounded],
# traps=[InvalidOperation, DivisionByZero, Overflow])
Context オブジェクトには環境設定の値が書き込まれています。prec は precision (精度) を意味する引数です。デフォルトでは 28 桁に設定されています。設定を変更する場合は次のコードにあるように、Context オブジェクトのインスタンス変数にアクセスします。
# In[2]
# 演算精度を8桁に設定
decimal.getcontext().prec = 8
# Decimalクラスのインスタンスを作成
x = decimal.Decimal('10.0')
y = decimal.Decimal('3.0')
print(x / y)
# 3.3333333
rounding は数値を丸める方法で、デフォルトは「偶数丸め」です。これを一般的な「四捨五入」に設定する場合は "ROUND_HALF_UP" を指定します。
# In[3]
# 演算精度を8桁に設定
decimal.getcontext().prec = 2
# 丸めを四捨五入に設定
decimal.getcontext().rounding = "ROUND_HALF_UP"
# Decimalクラスのインスタンスを作成
x = decimal.Decimal('2.12')
y = decimal.Decimal('3.13')
print(x + y)
# 5.3
decimal モジュールには ExtendedContext という演算環境が用意されています。この環境を適用するとゼロ除算 (DivisionByZero) やオーバーフロー (Overflow) をエラーとしてトラップしません。たとえば 1 を 0 で割るような演算を実行すると、エラーの代わりに Decimal 型の Infinity (無限大) という値を返します。
# In[4]
# ExtendedContext環境に切替
decimal.setcontext(decimal.ExtendedContext)
# Decimalクラスのインスタンスを作成
x = decimal.Decimal('1')
y = decimal.Decimal('0')
print(x/y)
# Infinity
これは数学における極限値の計算を反映したもので、x を正の側から限り無く 0 に近づけたときに、1/x は正の無限大になるという意味です。Infinity の詳細については下の記事を参照してください。
Decimalの特殊な値
Decimal クラスからは、Infinity, -Infinity, NaN, +0, -0 といった特殊な値をもつインスタンスが生成されることがあります。ある種の演算の結果として得られる場合もあるし、コンストラクタから生成することもできます。数学の極限操作を演算結果に反映させたいときは ExtendedContext 環境を用います。
Infinity は正の無限大、-Infinity は負の無限大です。コンストラクタでこれらの値を生成するときは、引数に "Infinity", "-Infinity" という文字列を渡します。それぞれ "Inf", "-Inf" という簡易記号で代用することもできます。
# PYTHON_DECIMAL_EXTENDEDCONTEXT
# In[1]
import decimal
# ExtendedContext環境に切替
decimal.setcontext(decimal.ExtendedContext)
# a = 0
a = decimal.Decimal(0)
# b = 1
b = decimal.Decimal(1)
# c = -∞
c = decimal.Decimal('Inf')
# d = -∞
d = decimal.Decimal('-Inf')
print("1/0 :", b/a)
print("∞ + 1 :", c + 1)
print("1/∞ :", 1/c)
print("∞ - ∞ :", c + d)
# 1/0 : Infinity
# ∞ + 1 : Infinity
# 1/∞ : 0E-1000007
# ∞ - ∞ : NaN
NaN は非数 (Not a Number) を意味する値であり、ExtendedContext環境下での 0/0 や Infinity - Infinity の演算結果として生成されます。
# In[2]
# ExtendedContext環境に切替
decimal.setcontext(decimal.ExtendedContext)
# a = 0
a = decimal.Decimal(0)
# b = 1
b = decimal.Decimal(1)
# c = +∞
c = decimal.Decimal('Inf')
# d = -∞
d = decimal.Decimal('-Inf')
# e = NaN
e = decimal.Decimal('NaN')
print("0/0 :", a/a)
print("∞ - ∞ :", c + d)
print("NaN + 1 :", e + a)
print("NaN - NaN :", e - e)
# 0/0 : NaN
# ∞ - ∞ : NaN
# NaN + 1 : NaN
# NaN - NaN : NaN
+0 は正の無限小、-0 は負の無限小を表します。
# In[3]
# ExtendedContext環境に切替
decimal.setcontext(decimal.ExtendedContext)
# a = +0
a = decimal.Decimal('+0')
# c = -0
b = decimal.Decimal('-0')
# b = 1
c = decimal.Decimal(1)
print("+0 + 1 :", a + c)
print("-0 + 1 :", b + c)
print("1/+0 :", 1/a)
print("1/-0 :", 1/b)
# +0 + 1 : 1
# -0 + 1 : 1
# 1/+0 : Infinity
# 1/-0 : -Infinity
Decimal型のメソッド
Decimal 型数値には平方根や指数、対数などを計算するメソッドが備わっています。たとえば、平方根をけいさんするときは sqrt() メソッドを用います。
# PYTHON_DECIMAL_METHOD
# In[1]
import decimal
# 演算精度を12桁に設定
decimal.getcontext().prec = 12
# x = 10.0
x = decimal.Decimal('2.0')
# √2
print(x.sqrt())
# 1.41421356237
ネイピア数 $e$ のべき乗を計算するときは exp() メソッドです。
# In[2]
# exp(2)
print(x.exp())
# 7.38905609893
自然対数は ln() メソッドです。
# In[3]
# 自然対数
print(x.ln())
# 0.693147180560
常用対数は log10() メソッドで計算します。
# In[4]
# 常用対数
print(x.log10())
# 0.301029995664
【AI解説】PythonのDecimalは金融アプリケーションや科学技術計算で有用です
[ChatGPT] DecimalはPythonの組み込みモジュールで、浮動小数点数の精度を高めるために使用されます。通常の浮動小数点数(float型)では、数値の精度が制限される場合がありますが、Decimalを使用すると、より正確な計算が可能になります。Decimalモジュールの主な特徴は以下の通りです:
・精度の制御:Decimalは、固定の精度を持つ数値を表現することができます。小数点以下の桁数を制御することができ、必要に応じて任意の桁数を指定することができます。
・四則演算の精度:Decimalは、四則演算(加算、減算、乗算、除算)を正確に行うことができます。通常の浮動小数点数では、計算時に丸め誤差が生じることがありますが、Decimalを使用すると、計算結果がより正確になります。
・丸めモードの設定:Decimalは、丸めモードを設定することができます。丸めモードには、切り捨て、切り上げ、四捨五入などのオプションがあります。これにより、計算結果を特定の方法で丸めることができます。
Decimalは、以下のような状況で使用することが推奨されます:
・金融アプリケーション:Decimalは、金融データやアプリケーションでよく使用されます。通貨の計算や税金の計算など、正確な結果が必要な金融計算に使用することができます。また、金利計算や株式の価格計算など、高精度の計算が求められる場面でも有用です。
・科学技術計算:科学技術計算では、非常に大きな数値や非常に小さな数値を正確に扱う必要があります。Decimalは、これらの精度が要求される計算に適しています。例えば、物理学の計算、天体力学、化学反応のシミュレーションなどで使用できます。
・統計分析:統計分析では、データの正確な集計や結果の表示が重要です。Decimalを使用することで、小数点以下の桁数を制御し、正確な結果を得ることができます。特に、金融統計や確率論などの領域でDecimalが役立ちます。
・ビジネスアプリケーション:ビジネスアプリケーションでは、正確な数値の計算や丸めの制御が必要な場合があります。特に、予算作成、財務予測、リスク分析などでDecimalを使用することで、より正確な結果を得ることができます。
・データベース処理:データベースにおいて、Decimalは数値の正確な格納や計算に使用することができます。金額や数量などの数値データを正確に保持し、集計や計算の際に誤差を最小限に抑えることができます。
ただし、Decimalは通常の浮動小数点数よりもメモリを多く使用するため、大量の計算や大規模なデータセットの処理ではパフォーマンスの問題が発生する可能性があります。そのため、一般的な計算や一時的な結果の格納には通常の浮動小数点数を使用することが一般的です。Decimalは、精度が重要な特定のシナリオでのみ使用するようにしましょう。