【Python基礎】小数を10進数として正しく扱うことができるDecimalモジュール

  • URLをコピーしました!
目次

Decimal

前回、Pythonで小数や整数を四捨五入するround関数を紹介しました。

今回は小数を10進数として正しく扱うことができるDecimalモジュールを紹介します。

ちなみにDecimal自体が10進数という意味です。

まずなぜDecimalという関数が必要かからみてみましょう。

とりあえず0.1に10回0.1を足してその値を表示するということをしてみます。

a = 0.1

for _ in range(10):
    print(a)
    a = a + 0.1

実行結果
0.1
0.2
0.30000000000000004
0.4
0.5
0.6
0.7
0.7999999999999999
0.8999999999999999
0.9999999999999999

この様にfloatで計算をすると、2進数で計算が行われるため、時々(とはいっても結構な頻度で)循環小数のような値となってしまうことがあります。

これを避けるため10進数の小数の計算を正確に行うモジュールが「Decimal」です。

それでは始めていきましょう。

Decimalの使い方

まずはDecimalモジュールを使って、小数を10進数の小数として認識させます。

そのためには「Decimal(str型での値)」とします。

from decimal import Decimal

a = Decimal(str(0.1))

for _ in range(10):
    print(a)
    b = Decimal(str(0.1))    
    a = a + b

実行結果
0.1
0.2
0.3
0.4
0.5
0.6
0.7
0.8
0.9
1.0

先ほどとは違い変な循環小数の様な値は出てきません。

Decimalモジュールを使うにあたって重要なのは引数に「Str型の値」を使うことです。

ここでfloat型の値を引数として与えてしまうと小数点以下の値がおかしな値で返ってきます。

from decimal import Decimal

a = Decimal(0.1)

for _ in range(10):
    print(a)
    b = Decimal(0.1)    
    a = a + b

実行結果
0.1000000000000000055511151231257827021181583404541015625
0.2000000000000000111022302463
0.3000000000000000166533453694
0.4000000000000000222044604925
0.5000000000000000277555756156
0.6000000000000000333066907387
0.7000000000000000388578058618
0.8000000000000000444089209849
0.9000000000000000499600361080
1.000000000000000055511151231

先ほどは「str(値)」でStr型に変換しましたが、「”値”」でも大丈夫です。

from decimal import Decimal

a = Decimal("0.1")

for _ in range(10):
    print(a)
    b = Decimal("0.1")    
    a = a + b

実行結果
0.1
0.2
0.3
0.4
0.5
0.6
0.7
0.8
0.9
1.0

小数点以下の値の四捨五入をするquantize関数

Decimalモジュールで小数点以下の値を四捨五入するには「quantize関数」を用います。

使い方は「Decimal型の小数.quantize(Decimal(“小数の桁数”))」で、小数の桁数は「0.1」や「0.01」といった形で表します。

from decimal import Decimal

a = Decimal("123456789.123456789")

print(a.quantize(Decimal("0.1")))
print(a.quantize(Decimal("0.01")))
print(a.quantize(Decimal("0.001")))
print(a.quantize(Decimal("0.0001")))
print(a.quantize(Decimal("0.00001")))
print(a.quantize(Decimal("0.000001")))
print(a.quantize(Decimal("0.0000001")))
print(a.quantize(Decimal("0.00000001")))
print(a.quantize(Decimal("0.000000001")))
print(a.quantize(Decimal("0.0000000001")))

実行結果
123456789.1
123456789.12
123456789.123
123456789.1235
123456789.12346
123456789.123457
123456789.1234568
123456789.12345679
123456789.123456789
123456789.1234567890

指定した桁数が元の値の桁数を超えると最後に0が追加されます。

ちなみにquantizeの引数にfloat型の値を与えるとエラーとなります。

from decimal import Decimal

a = Decimal("123456789.123456789")

print(a.quantize(0.1))

実行結果
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[7], line 5
      1 from decimal import Decimal
      3 a = Decimal("123456789.123456789")
----> 5 print(a.quantize(0.1))

TypeError: conversion from float to Decimal is not supported

またround関数とは違って、整数側の四捨五入には対応していません。

from decimal import Decimal

a = Decimal("123456789.123456789")

print(a.quantize(Decimal("1")))
print(a.quantize(Decimal("10")))
print(a.quantize(Decimal("100")))
print(a.quantize(Decimal("1000")))
print(a.quantize(Decimal("10000")))
print(a.quantize(Decimal("100000")))
print(a.quantize(Decimal("1000000")))
print(a.quantize(Decimal("10000000")))
print(a.quantize(Decimal("100000000")))
print(a.quantize(Decimal("1000000000")))
print(a.quantize(Decimal("10000000000")))

実行結果
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789

値の丸め方の指定

quantizeでは値の丸め方を指定することができます。

その場合は引数に「rounding」というオプション引数を追加します。

またその引数の値はインポートする必要があります。

まずは丸め方を指定しない状態からどう丸められるのかみてみましょう。

指定なし:絶対値に対して四捨五入

from decimal import Decimal

a = Decimal("1.4")
b = Decimal("1.5")
c = Decimal("1.6")
d = Decimal("-1.4")
e = Decimal("-1.5")
f = Decimal("-1.6")

print(a.quantize(Decimal("1")))
print(b.quantize(Decimal("1")))
print(c.quantize(Decimal("1")))
print(d.quantize(Decimal("1")))
print(e.quantize(Decimal("1")))
print(f.quantize(Decimal("1")))

実行結果
1
2
2
-1
-2
-2

ROUND_CEILING:無限大側に切り上げ

from decimal import Decimal, ROUND_CEILING

a = Decimal("1.4")
b = Decimal("1.5")
c = Decimal("1.6")
d = Decimal("-1.4")
e = Decimal("-1.5")
f = Decimal("-1.6")

print(a.quantize(Decimal("1"), rounding=ROUND_CEILING))
print(b.quantize(Decimal("1"), rounding=ROUND_CEILING))
print(c.quantize(Decimal("1"), rounding=ROUND_CEILING))
print(d.quantize(Decimal("1"), rounding=ROUND_CEILING))
print(e.quantize(Decimal("1"), rounding=ROUND_CEILING))
print(f.quantize(Decimal("1"), rounding=ROUND_CEILING))

実行結果
2
2
2
-1
-1
-1

ROUND_FLOOR:無限小側に切り下げ

from decimal import Decimal, ROUND_FLOOR

a = Decimal("1.4")
b = Decimal("1.5")
c = Decimal("1.6")
d = Decimal("-1.4")
e = Decimal("-1.5")
f = Decimal("-1.6")

print(a.quantize(Decimal("1"), rounding=ROUND_FLOOR))
print(b.quantize(Decimal("1"), rounding=ROUND_FLOOR))
print(c.quantize(Decimal("1"), rounding=ROUND_FLOOR))
print(d.quantize(Decimal("1"), rounding=ROUND_FLOOR))
print(e.quantize(Decimal("1"), rounding=ROUND_FLOOR))
print(f.quantize(Decimal("1"), rounding=ROUND_FLOOR))

実行結果
1
1
1
-2
-2
-2

ROUND_UP:絶対値に対して切り上げ

from decimal import Decimal, ROUND_UP

a = Decimal("1.4")
b = Decimal("1.5")
c = Decimal("1.6")
d = Decimal("-1.4")
e = Decimal("-1.5")
f = Decimal("-1.6")

print(a.quantize(Decimal("1"), rounding=ROUND_UP))
print(b.quantize(Decimal("1"), rounding=ROUND_UP))
print(c.quantize(Decimal("1"), rounding=ROUND_UP))
print(d.quantize(Decimal("1"), rounding=ROUND_UP))
print(e.quantize(Decimal("1"), rounding=ROUND_UP))
print(f.quantize(Decimal("1"), rounding=ROUND_UP))

実行結果
2
2
2
-2
-2
-2

ROUND_DOWN:0に向かって切り捨て

from decimal import Decimal, ROUND_DOWN

a = Decimal("1.4")
b = Decimal("1.5")
c = Decimal("1.6")
d = Decimal("-1.4")
e = Decimal("-1.5")
f = Decimal("-1.6")

print(a.quantize(Decimal("1"), rounding=ROUND_DOWN))
print(b.quantize(Decimal("1"), rounding=ROUND_DOWN))
print(c.quantize(Decimal("1"), rounding=ROUND_DOWN))
print(d.quantize(Decimal("1"), rounding=ROUND_DOWN))
print(e.quantize(Decimal("1"), rounding=ROUND_DOWN))
print(f.quantize(Decimal("1"), rounding=ROUND_DOWN))

実行結果
1
1
1
-1
-1
-1

ROUND_HALF_UP:四捨五入

from decimal import Decimal, ROUND_HALF_UP

a = Decimal("1.4")
b = Decimal("1.5")
c = Decimal("1.6")
d = Decimal("-1.4")
e = Decimal("-1.5")
f = Decimal("-1.6")

print(a.quantize(Decimal("1"), rounding=ROUND_HALF_UP))
print(b.quantize(Decimal("1"), rounding=ROUND_HALF_UP))
print(c.quantize(Decimal("1"), rounding=ROUND_HALF_UP))
print(d.quantize(Decimal("1"), rounding=ROUND_HALF_UP))
print(e.quantize(Decimal("1"), rounding=ROUND_HALF_UP))
print(f.quantize(Decimal("1"), rounding=ROUND_HALF_UP))

実行結果
1
2
2
-1
-2
-2

ROUND_HALF_DOWN:五捨六入(5以下は切り捨て、6以上は切り上げ)

from decimal import Decimal, ROUND_HALF_DOWN

a = Decimal("1.4")
b = Decimal("1.5")
c = Decimal("1.6")
d = Decimal("-1.4")
e = Decimal("-1.5")
f = Decimal("-1.6")

print(a.quantize(Decimal("1"), rounding=ROUND_HALF_DOWN))
print(b.quantize(Decimal("1"), rounding=ROUND_HALF_DOWN))
print(c.quantize(Decimal("1"), rounding=ROUND_HALF_DOWN))
print(d.quantize(Decimal("1"), rounding=ROUND_HALF_DOWN))
print(e.quantize(Decimal("1"), rounding=ROUND_HALF_DOWN))
print(f.quantize(Decimal("1"), rounding=ROUND_HALF_DOWN))

実行結果
1
1
2
-1
-1
-2

ROUND_HALF_EVEN:近い方に丸めるが、同じ場合は偶数整数方向への丸め

from decimal import Decimal, ROUND_HALF_EVEN

a = Decimal("1.4")
b = Decimal("1.5")
c = Decimal("1.6")
d = Decimal("-1.4")
e = Decimal("-1.5")
f = Decimal("-1.6")

print(a.quantize(Decimal("1"), rounding=ROUND_HALF_EVEN))
print(b.quantize(Decimal("1"), rounding=ROUND_HALF_EVEN))
print(c.quantize(Decimal("1"), rounding=ROUND_HALF_EVEN))
print(d.quantize(Decimal("1"), rounding=ROUND_HALF_EVEN))
print(e.quantize(Decimal("1"), rounding=ROUND_HALF_EVEN))
print(f.quantize(Decimal("1"), rounding=ROUND_HALF_EVEN))

実行結果
1
2
2
-1
-2
-2

ROUND_05UP:0か5なら0から遠い方向に、それ以外はゼロ方向に丸め

from decimal import Decimal, ROUND_05UP

a = Decimal("1.4")
b = Decimal("1.5")
c = Decimal("1.6")
d = Decimal("-1.4")
e = Decimal("-1.5")
f = Decimal("-1.6")

print(a.quantize(Decimal("1"), rounding=ROUND_05UP))
print(b.quantize(Decimal("1"), rounding=ROUND_05UP))
print(c.quantize(Decimal("1"), rounding=ROUND_05UP))
print(d.quantize(Decimal("1"), rounding=ROUND_05UP))
print(e.quantize(Decimal("1"), rounding=ROUND_05UP))
print(f.quantize(Decimal("1"), rounding=ROUND_05UP))

実行結果
1
1
1
-1
-1
-1

次回は1次元リストを指定した個数ずつに分割した2次元リストに変換する方法を紹介します。

ではでは今回はこんな感じで。

よかったらシェアしてね!
  • URLをコピーしました!

コメント

コメントする

目次