[Python] 少数を含む金額計算を正確に行う(decimalモジュールの利用)
こんにちは、@yoheiMuneです。
最近はシステムトレードのプログラムを書いていて、金額計算などで少数を正確に扱う必要があり、decimalモジュールを利用しました。今日のブログではそのDecimalの使い方をブログに残したいと思います。
パソコンでは数値を2進数で表現しますが、2進数では
なんでもない数値を扱うときは、2進数で表現できない数値の誤差は気にしないのですが、お金とかを扱う場合には正しく扱いたいものです。その際にdecimalモジュールを使うことで、0.1などを正確に扱うことができるようになります。
演算した結果の数値を丸める方は、以下のように指定することができます。
https://docs.python.org/ja/3/library/decimal.html
最後になりますが本ブログでは、Python・Linux・フロントエンド・Node.js・インフラ・開発環境・Go言語・Swift・Java・機械学習など雑多に情報発信をしていきます。自分の第2の脳にすべく、情報をブログに貯めています。気になった方は、本ブログのRSSやTwitterをフォローして頂けると幸いです ^ ^。
最後までご覧頂きましてありがとうございました!
最近はシステムトレードのプログラムを書いていて、金額計算などで少数を正確に扱う必要があり、decimalモジュールを利用しました。今日のブログではそのDecimalの使い方をブログに残したいと思います。
目次
decimalモジュールとは
decimalモジュールは、少数を含む数値を正確に表現したり計算したりすることに使えるモジュールです。パソコンでは数値を2進数で表現しますが、2進数では
0.1
という表現(1の10分の1)を正確に表すことができません。num = 1.1 print(type(num)) # <class 'float'> => float(浮動点少数で扱っている)参考:1より小さい数を含む二進数表現
なんでもない数値を扱うときは、2進数で表現できない数値の誤差は気にしないのですが、お金とかを扱う場合には正しく扱いたいものです。その際にdecimalモジュールを使うことで、0.1などを正確に扱うことができるようになります。
decimalモジュールを使って、0.1を表現する
decimalモジュールを使うと、10進数の0.1を浮動点少数(floating point)ではなく、固定点少数(fixed point)で扱うことができます。# decimalで0.1を表現する方法 from decimal import Decimal num = Decimal("0.1") print(num) # 0.1ここでポイントとしては、
"0.1"
と文字列で指定することで、固定点少数で扱うことができます。0.1
とfloat型で指定すると、decimalは浮動点少数で値を保持します。# decimalで浮動点少数で0.1を表現する from decimal import Decimal num = Decimal(0.1) print(num) # 0.1000000000000000055511151231257827021181583404541015625固定点少数の場合と、保持している数値が異なることがわかります。ということで、0.1を正確に扱うためには
Decimal("0.1")
とする必要があります。decimalでの演算
Decimal
クラスには演算子が定義されているので、普通の数値のように演算することができます。from decimal import Decimal # Decimalで演算をする例 Decimal("0.1") + Decimal("0.2") # Decimal('0.3') Decimal("0.1") - Decimal("0.2") # Decimal('-0.1') Decimal("0.1") * Decimal("0.2") # Decimal('0.02') Decimal("0.1") / Decimal("0.2") # Decimal('0.5')
演算結果の精度を指定する
また、演算した結果、どれだけの精度で保持するかを指定することもできます。from decimal import Decimal, getcontext # デフォルトでは少数28桁まで保持する. Decimal(1) / Decimal(7) # Decimal('0.1428571428571428571428571429') # 精度を指定することもできる(以下では少数第3位まで). getcontext().prec = 3 Decimal(1) / Decimal(7) # Decimal('0.143')
floatトラップで安全に計算する
そのほかに、安全のため、Decimalの計算で、うっかりfloatを混ぜないように(混ぜたらエラーになるように)設定することができます。from decimal import Decimal, getcontext, FloatOperation # floatトラップを有効にする getcontext().traps[FloatOperation] = True # floatで初期化するとエラー Decimal(3.14) # Traceback (most recent call last): # File "<stdin>", line 2, in <module> # decimal.FloatOperation: [<class 'decimal.FloatOperation'>] # floatが演算に混ざるとエラー Decimal(2) < 0.5 # Traceback (most recent call last): # File "<stdin>", line 1, in <module> # decimal.FloatOperation: [<class 'decimal.FloatOperation'>]
数値の丸め方を指定する
制度からはみ出す数値をどのように丸めるのか、これも金額計算では重要なポイントです(例えば日本で消費税は小数点以下を切り捨てる、など)。演算した結果の数値を丸める方は、以下のように指定することができます。
from decimal import Decimal, getcontext, ROUND_UP # 有効桁数を3 getcontext().prec = 3 # デフォルトでは、ROUND_HALF_EVEN(近い方に、引き分けは偶数整数方向に向けて丸めます) getcontext().rounding # ROUND_HALF_EVEN Decimal(1) / Decimal(3) # Decimal('0.333') # 例えば、ROUND_FLOOR(ゼロから遠い方向に丸めます。)に変更できます. getcontext().rounding = ROUND_UP Decimal(1) / Decimal(3) # Decimal('0.334')丸め方のそのほかの指定は、「ドキュメント - 9.4.5. 丸めモード」をご確認ください。
参考資料
Pythonの公式ドキュメントが非常に参考になりますので、詳細はそちらをご確認ください。https://docs.python.org/ja/3/library/decimal.html
最後に
金額計算の実装はなかなかスリリングですが、正確にできると楽しいですね。人のお金を扱うプログラミングはまだまだ書ける気がしませんが、自分のお金を扱いつつパワーアップできたらと思います。今後もPythonはドシドシと取り組んでいきたい言語です。最後になりますが本ブログでは、Python・Linux・フロントエンド・Node.js・インフラ・開発環境・Go言語・Swift・Java・機械学習など雑多に情報発信をしていきます。自分の第2の脳にすべく、情報をブログに貯めています。気になった方は、本ブログのRSSやTwitterをフォローして頂けると幸いです ^ ^。
最後までご覧頂きましてありがとうございました!