アーカイブ
若手社員たちのおかげで自社のブログの更新が活性化してきたので、中年Pythonエンジニアからも久々にひとネタ投入
はじめに
今回は元々PHP畑出身のエンジニアだった私が、遥か昔にPython初学者の際にかかった罠についてのお話しです。
経緯
Python初学者だったころ、「PHPのこの関数はPythonだと何に当たるのだろう」といった感じでインターネットでシコシコ調べながら実装していました。
この日も、とある四捨五入が必要な計算ロジックを組んでいる際、頭の中では「PHPだったらこう書くよな」と思い描きながら実装を行っていました。
「Pythonにもround関数ってあるよな〜、調べてみるか」(PHPerとしては当然の所作)と思いながらインターネットを検索していると思った通り、Pythonにもround関数は存在していたので、「じゃあ、これ使えば良いか」と特にround関数の挙動については確認せずに実装し、動作確認を行っていました。
あれ?何やら数値がずれたり、想定通りだったりと、何かがおかしい。
しかし、計算式を見ても特におかしなところは見当たらない。
「え、なんで? 俺のロジック間違ってる!?」
と半信半疑になりつつ、printデバッグを仕込んで値をひとつずつ確かめていくと、原因らしきものが見えてきました。
どうも round() の返す値が、私の知っている四捨五入と一致していないのです。
print(round(2.5))
2
「え? 2? なんで???」
この瞬間、当時の私は本気で「Pythonのround関数バグってる?」と思いました。
細かいロジックを見ても問題なし。計算式にも異常なし。にもかかわらず、どう見ても合わない。
最終的に「これはもう自分の理解が間違っているに違いない」と悟り、ようやく公式ドキュメントを確認することに。
当時の自分にとって衝撃的でした。
「Python の round() は "偶数への丸め (Banker's Rounding)" を採用します。」
「…偶数への丸め? 何それ??」
「Banker's Rounding」とはつまり、.5 は常に切り上げられるわけではなく、「近い偶数に丸められる」というのが Python の仕様だったのです。
当時の私は呆然としつつ、「知らんがな!」と心の中で叫んだのを今でも覚えています。
最後に
この記事を読んで、もうこれ以上犠牲者が増えないことを祈り、Python の round 結果の具体例と Python での一般的な四捨五入(.5は常に切り上げる)の実装例を載せておきます。
Python の round() 結果の具体例
print(round(0.5)) # トラップ発動!
0
print(round(1.5))
2
print(round(2.5)) # トラップ発動!
2
print(round(3.5))
4
Python での一般的な四捨五入(.5は常に切り上げる)の実装例
from decimal import Decimal, ROUND_HALF_UP
def round_half_up(val, digits=0):
"""
四捨五入
val : 対象の数値
digits : 丸めたい桁数(例:2 → 小数第2位)
"""
quant = Decimal('1').scaleb(-digits)
return float(Decimal(str(val)).quantize(quant, rounding=ROUND_HALF_UP))
積極採用中!尖ったPythonエンジニアへの第一歩はこちらから


