『Python数値計算ノート』ではアフィリエイトプログラムを利用して商品を紹介しています。

Decimal.quantizeによる端数処理

Decimal.quantize()

十進浮動小数点数 (Decimalオブジェクト) の quantize()メソッドを用いると、丸める方法を指定して端数を処理することができます。

Decimal.quantize(m, rounding)

m には小数点以下に残す桁数を Decimal型で指定します。
小数部分を捨てるなら Decimal(“0”) と記述します。
小数点以下 1 桁を残すなら Decimal(“0.1”) です。
rounding 引数には丸める方法を指定します。
デフォルトでは ROUND_HALF_EVEN となっています。

# PYTHON_DECIMAL_QUANTIZE

# In[1]

from decimal import Decimal

x = Decimal("10.5")
y = Decimal("11.5")

a = x.quantize(Decimal("0"))
b = y.quantize(Decimal("0"))

print(a, b)
# 10 12

丸めモードについては以下を参照してください。

丸めモードの種類

quantize() の丸めモードは decimalモジュールに記述されているクラスなので、使用する場合は予めインポートしておく必要があります。

decimal.ROUND_CEILING

数値を切り上げます。戻り値は常にもとの数値より大きくなっていなければなりません。すなわち、負数が渡されたときは 0 に近いほうへ丸められます。このような丸め方を、公式ドキュメントでは「正の無限大 (Infinity) 方向へ丸める」と表現しています(数直線のプラス側のほうへ丸めるという意味です)。

# DECIMAL_QUANTIZE_ROUND_CEILING

# In[1]

from decimal import Decimal, ROUND_CEILING

x = Decimal("10.5")
y = Decimal("-10.5")

a = x.quantize(Decimal("0"), rounding=ROUND_CEILING)
b = y.quantize(Decimal("0"), rounding=ROUND_CEILING)

print(a, b)
# 11 -10

decimal.ROUND_FLOOR

数値を切り捨てます。戻り値は常にもとの数値より小さくなっているはずです。すなわち、負数が渡されたときは 0 から遠いほうへ丸められます。公式ドキュメントでは「負の無限大 (Infinity) 方向へ丸める」と表現しています(数直線のマイナス側へ丸めるという意味です)。

# DECIMAL_QUANTIZE_ROUND_FLOOR

# In[1]

from decimal import Decimal, ROUND_FLOOR

x = Decimal("10.5")
y = Decimal("-10.5")

a = x.quantize(Decimal("0"), rounding=ROUND_FLOOR)
b = y.quantize(Decimal("0"), rounding=ROUND_FLOOR)

print(a, b)
# 10 -11

decimal.ROUND_DOWN

原点へ向かう方へ丸めます。つまり正の値 10.5 が渡されたときは原点に近い 10 へ丸められます。負の値 -10.5 が渡されたときは、やはり原点に近いほうの -10 へ丸められます。渡す値の符号によって丸める方向が異なることに注意してください。

# DECIMAL_QUANTIZE_ROUND_DOWN

# In[1]

from decimal import Decimal, ROUND_DOWN

x = Decimal("10.5")
y = Decimal("-10.5")

a = x.quantize(Decimal("0"), rounding=ROUND_DOWN)
b = y.quantize(Decimal("0"), rounding=ROUND_DOWN)

print(a, b)
# 10 -10

decimal.ROUND_HALF_DOWN

渡された数値に最も近い整数へ丸められます。しかし 10.5 は 10 にも 11 にも等距離です。このような場合は 0 に近いほうへ丸められます。

# DECIMAL_QUANTIZE_ROUND_HALF_DOWN

# In[1]

from decimal import Decimal, ROUND_HALF_DOWN

x = Decimal("10.1")
y = Decimal("10.5")
z = Decimal("10.9")

a = x.quantize(Decimal("0"), rounding=ROUND_HALF_DOWN)
b = y.quantize(Decimal("0"), rounding=ROUND_HALF_DOWN)
c = z.quantize(Decimal("0"), rounding=ROUND_HALF_DOWN)

print(a, b, c)
# 10 10 11

decimal.ROUND_HALF_EVEN

渡された数値に最も近い整数へ丸められます。しかし、10.5 は 10 にも 11 にも等距離です。このような場合は偶数のほうへ丸められます。

# DECIMAL_QUANTIZE_ROUND_HALF_EVEN

# In[1]

from decimal import Decimal, ROUND_HALF_EVEN

x = Decimal("10.5")
y = Decimal("11.5")

a = x.quantize(Decimal("0"), rounding = ROUND_HALF_EVEN)
b = y.quantize(Decimal("0"), rounding = ROUND_HALF_EVEN)

print(a, b)
# 10 12

decimal.ROUND_HALF_UP

渡された数値に最も近い整数に丸められます。等距離の整数が 2 つある場合は 0 から遠いほうを選択します。これは一般的に知られる「四捨五入」です。

# DECIMAL_QUANTIZE_ROUND_HALF_UP

# In[1]

from decimal import Decimal, ROUND_HALF_UP

x = Decimal("10.1")
y = Decimal("10.5")
z = Decimal("10.9")

a = x.quantize(Decimal("0"), rounding=ROUND_HALF_UP)
b = y.quantize(Decimal("0"), rounding=ROUND_HALF_UP)
c = z.quantize(Decimal("0"), rounding=ROUND_HALF_UP)

print(a, b, c)
# 10 11 11

decimal.ROUND_UP

渡された数値を 0 から遠いほうの整数に丸めます。たとえば正の値 10.5 が渡されたときは原点から遠い 11 へ丸められます。負の値 -11 が渡されたときは、やはり原点に近いほうの -11 へ丸められます。渡した値の符号によって丸める方向が異なることに注意してください(一般に、このような丸め方で統計処理などを行なうとバイアスが生じます)。

# DECIMAL_QUANTIZE_ROUND_UP

# In[1]

from decimal import Decimal, ROUND_UP

x = Decimal("10.5")
y = Decimal("-10.5")

a = x.quantize(Decimal("0"), rounding=ROUND_UP)
b = y.quantize(Decimal("0"), rounding=ROUND_UP)

print(a, b)
# 11 -11

decimal.ROUND_05UP

まず渡された数値を 0 方向に丸めます。そのとき、最後の桁が 0 または 5 であるときには遠いほうの整数に丸めます(すなわち 1 を加えます)。たとえば 15.5 を 0 に近いほうに丸めると 15 となります。末尾が 5 なので、1 を加えて 16 とします。

# DECIMAL_QUANTIZE_ROUND_05UP

# In[1]

from decimal import Decimal, ROUND_05UP

x = Decimal("10.5")
y = Decimal("11.5")
z = Decimal("15.5")

a = x.quantize(Decimal("0"), rounding=ROUND_05UP)
b = y.quantize(Decimal("0"), rounding=ROUND_05UP)
c = z.quantize(Decimal("0"), rounding=ROUND_05UP)

print(a, b, c)
# 11 11 16

演算コンテキストの活用

あるモジュール内、あるいはパッケージ全体において、端数処理の仕方は統一しておきたいはずです。Context() コンストラクタで独自の演算コンテキストを作成しておけば、そのモジュールでどのような端数処理を採用しているのか明確になります。

# DECIMAL_QUANTIZE_SET_CONTEXT

# In[1]

from decimal import Decimal, Context, ROUND_HALF_UP, setcontext

# Contexを作成
my_context = Context(prec = 64, rounding = ROUND_HALF_UP)

# 作成したContext環境への切り替え
setcontext(my_context)

x = Decimal("10.5")

# my_contex環境にしたがって端数処理
a = x.quantize(Decimal("0"))

print(a)
# 11

コメント

  1. tanner より:

    roundingが渡されなかった場合のデフォルトは、ROUND_HALF_EVENのようです。
    https://docs.python.org/ja/3/library/decimal.html#decimal.DefaultContext

  2. あとりえこばと より:

    正確な情報をありがとうございます。
    記事は修正しておきました。m(_ _)m