Pythonのround()は銀行丸め

概要

Pythonでとあるデータについて四捨五入していた際、予想と異なる結果が出てきて調べたのでまとめた。

要約

Pythonのround()銀行丸めなので注意。

再現コード

>>> round(0.5)  
0  
>>> round(1.5)  
2  
>>> round(2.5)  
2  
>>> round(3.5)  
4  

解説

実はPythonのドキュメントにもこのことは明記されている

浮動小数点数に対する round() の振る舞いは意外なものかもしれません: 例えば、 round(2.675, 2) は予想通りの 2.68 ではなく 2.67 を与えます。これはバグではありません: これはほとんどの小数が浮動小数点数で正確に表せないことの結果です。

じゃあいわゆる一般的な四捨五入を行いたい場合はどうするか。

>>> round = lambda x: (x * 2 + 1) // 2  
>>> round(0.5)  
1.0  
>>> round(1.5)  
2.0  
>>> round(2.5)  
3.0  
>>> round(3.5)  
4.0  

lambdaを使う(参考)。

まとめ

この「銀行丸め」はPythonに限らず、結構いろんなところで話題になったりする。身近なところだと、Excelのシート関数であるround()とVBAのround()は挙動が異なっていて、前者が一般的な四捨五入であるのに対し、後者は銀行丸めとして実装されている。

銀行丸め自体はWikipediaの該当ページにも記述されているとおり、JISやISOにも規定されたれっきとした処理だったりする。

JIS Z 8401で定められていることから「JISの丸め方」、あるいは同様にISO 31-0で定められていることから「ISO丸め」ともいう。

「銀行」という名称のとおり、丸め処理を行う前後の差が大きくなることを嫌った銀行員が好んで使った、という話からもこの処理が実用的かが伺える。

そんなわけで、「round()は四捨五入」とだけ覚えていると若干ハマるかも、というお話。