0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

アクチュアリーのためのPython入門(なぜdecimalを使うのか)

Last updated at Posted at 2026-01-13

アクチュアリーのためのPython入門(第5回)

なぜPythonのroundではなくdecimalを使うのか

はじめに

前回までで、生命表・基数表・給付現価・保険料・責任準備金と、
一通りの計算をPythonで行いました。

今回は少し毛色を変えて、**計算結果の「丸め」**について扱います。

これは練習レベルではあまり問題になりませんが、

  • 実務での営業保険料、責任準備金
  • 解約返戻金

では、無視できない重要な論点です。

Pythonのroundは「普通の四捨五入」ではない

まず、多くの人が直感的に「四捨五入」だと思っているround()ですが、
Pythonのround()は偶数丸めという方式を使っています。

例を見てみます。

print(round(1.5))
print(round(2.5))

結果は、

  • round(1.5) → 2
  • round(2.5) → 2

となります。

2
2

「0.5は切り上げる」と思っていると、
2.5が2になるのは違和感がありますよね。

これは、
ちょうど0.5のときは、偶数になる方に丸める
というルールとなっているからです。

なぜ偶数丸めが使われるのか

偶数丸めは、計算を大量に繰り返したときに
切り上げ・切り下げの偏りを減らすという利点があります。

統計や科学の計算では、とても合理的な方法です。
ただし、生命保険の実務では話が変わります。

生命保険の計算では、保険料や解約返戻金など、
お金そのものを扱います。
このとき重要なのは、

  • 計算方法が説明できること
  • Excelなど他のソフトウェアでも計算結果が一致すること

です。0.5ならば必ず切り上げなければなりません。

もう一つの問題:浮動小数点の誤差

さらに、もう一つ大事な問題があります。

Pythonの数値の形式float(Excelではdoubleに相当)は、
内部では数値を2進数で扱っています。
そのため、10進数では「きれいに割り切れる数」でも、
内部では近似になります。
例えば、

print(round(102.03-100.18,1))

とすると、102.03-100.18=1.85なので、
小数第2位を四捨五入して、1.9になるはずですが、結果は、

1.8

になります。

この現象はPython特有ではありません。
ExcelやAccessでも同様のことが起こりえます。

実務でよくある対処法

実務ではこの問題を避けるために、
最終結果に影響を与えないようなごく小さい数(例:1E-10)を
足してから丸める独自の四捨五入関数を作る
といった対応をします。

Excel VBAでも、Round関数をそのまま使わず、
自作関数を使うケースは珍しくありません。
Pythonでも同様に、decimalモジュールを使う方法があります。

decimal を使った四捨五入

以下が、今回使った四捨五入関数です。

# 端数処理 四捨五入
# decimalのインポート
from decimal import Decimal, ROUND_HALF_UP, getcontext
# 四捨五入の関数
def roundhu(value, ndigits):
    getcontext().prec = 28
    d = Decimal(str(value))
    quant = Decimal("1").scaleb(-ndigits)
    return d.quantize(quant, rounding=ROUND_HALF_UP)

分割して説明を付け加えていきます。

from decimal import Decimal, ROUND_HALF_UP, getcontext
  • Decimal
    → 10進数をそのまま扱う数値型
  • ROUND_HALF_UP
    → 一般的な四捨五入
  • getcontext
    → decimal計算全体の設定(精度など)を管理する
getcontext().prec = 28
  • decimald計算で使う有効桁数を指定
  • 28桁はdecimalのデフォルトで、実務では十分な精度
  • ここでは「精度不足による誤差」を避けるため明示的に設定
d = Decimal(str(value))

ここが非常に重要です。

  • Decimal(value)とすると、floatの誤差をそのまま引き継ぐ
  • str(value)にしてからDecimalに渡すことで
    見た目通りの10進数として扱える
quant = Decimal("1").scaleb(-ndigits)
  • scaleb(n)は10のn乗を掛ける操作
  • 例として、ndigits=2の場合は、
    → Decimal("1").scaleb(-2)=Decimal("0.01")
    つまり、「小数点第何位で丸めるか」を指定しています。
return d.quantize(quant, rounding=ROUND_HALF_UP)
  • quantize
    → 指定した桁数で丸める
  • ROUND_HALF_UP
    → 5は必ず切り上げ(実務では一般的)

この結果として、
ExcelやPython標準のroundとは異なり、
意図通りの四捨五入ができます。

まとめ

今回は、最終的な端数処理に着目して
Decimal型を使った四捨五入の方法を紹介しました。

実務では、途中計算からDecimal型を使うケースもありますが、
本連載では分かりやすさを優先し、
これまではfloat型のまま計算しています。

次回は、これまで作ってきた計算を
Decimal型に置き換えた実装例を紹介します。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?