残念な四捨五入処理!四捨五入プログラムで気をつけなければいけない理由[Python]

2021-08-28
管理人代理

先生!この人たち銀行丸めです!!!

上司

ちょっと落ち着いてくれる?
一体何をしたの?

管理人代理

単純に四捨五入のコードを書いてただけです……。

問題のコード

result=round(2.5)

こちら、実行するとresultに何が入るか分かります?

上司

2.5の四捨五入だから……3?

管理人代理

残念!不正解です。

こちらのメソッドは「銀行丸め」なので正解は「2」です!

銀行丸めとは

ざっくりいうと2.5を四捨五入した時に2に変えてしまう丸め方。

統計的な処理であれば、四捨五入ではなく0.5の場合は近い偶数に近づけた方が計算しやすいし誤差が減るらしいです。

銀行丸め前銀行丸め後
1.52
2.52
3.54
4.54
5.56
小数点以下銀行丸めをした時に返ってくる値

Pythonではそのまま四捨五入のメソッドを使うと四捨五入されない

実は四捨五入のround関数って色々な言語で実装されてはいるんですけど、初期状態は銀行丸めなこと、結構あります。
Pythonは上で紹介した通り、銀行丸めになってしまうのですが、実はPython以外でもC#というか.net全般でも同じことが起こります。

C#でもPythonでも四捨五入メソッドを適当に簡単そうなものを選ぶと失敗するのは共通です。

上司

嫌なお揃いだ……。

C#の場合は、引数を渡さないと一般的にいう四捨五入にはならないし、Pythonなんてround使ってたら普通の四捨五入ができない。
一応、銀行丸めは誤差を減らすための計算方法らしいですが、正直誤動作の原因になっているので何も設定しないと自動で銀行丸めにされてしまう挙動は個人的にはあんまり好きじゃないです。
気が付かずに会計システムに組み込んでしまった日には地獄ですね……。

JavaはJavaでroundを使用すると四捨五入する桁数を指定できないっていうなかなか癖のある作りをしていますが、まあこの作りで事故ることはないのでヨシ!

ちなみに、「Javaで動くPython」をテーマに作られたJythonは実態がJavaなのでround関数は普通に四捨五入されました。こういう言語によって同じ名前で微妙に動きが違うの、よくないと思うの。

対策方法

設計仕様に四捨五入が出てきた場合はどんなプログラミング言語で実装するにしても2.5が四捨五入されるようにテストを設定しておきましょう。
最悪はテストさえしていれば避けられます!

Pythonであれば、以下のようなコードを使えば広く使われている四捨五入処理になります。
コードレビューも大切ですね。

from decimal import Decimal, ROUND_HALF_UP, ROUND_HALF_EVEN

result=Decimal(str(2.5)).quantize(Decimal('0'), rounding=ROUND_HALF_UP)

そして以下のプログラミング言語を使うときには「四捨五入」というワードが出てきたら、銀行丸めではないか注意した方が良いです。

  • python
  • C#
  • VBA
  • MySQL