Python で数値の計算をしているときに、小数点以下を四捨五入するなどして整数にしたいタイミングがあります。
そのようなタイミングでまず使うのが、標準で用意されている round()
という関数でしょう。
しかし、この関数、ちょっと変わった動きをすることがあります。
round()
を使ってみる例
例えば、3.5 という小数を整数にしたいとします。
print(round(3.5))
このようなコードを書くと、どうなるでしょうか。
4
このように表示されます。これは想定通りですね。
では、次は 2.5 という小数で試してみましょう。
print(round(2.5))
2
……!? なぜか、2.5 を四捨五入した結果の 3 にはなりません。これはどういうことでしょうか。
Python のマニュアルを読む
round()
について説明した Python 公式のマニュアルを読んでみます。執筆時点で最新のものを参照しています。
https://docs.python.org/ja/3.13/library/functions.html#round
round(number, ndigits=None)
number を小数点以下 ndigits 桁の精度で丸めた値を返します。ndigits が省略されたり、
None
だった場合、入力値に最も近い整数を返します。round()
をサポートする組み込み型では、値は 10 のマイナス ndigits 乗の倍数の中で最も近いものに丸められます; 二つの倍数が同じだけ近いなら、偶数を選ぶ方に (そのため、例えばround(0.5)
とround(-0.5)
は両方とも0
に、round(1.5)
は2
に) 丸められます。 ndigits には任意の整数値が有効となります (正の整数、ゼロ、負の整数)。 返り値は ndigits が指定されていないかNone
の場合は整数、そうでなければ返り値は number と同じ型です。
何やら難しげなことが書いてありますが、1 行ずつ読んでみましょう。
まず、
number を小数点以下 ndigits 桁の精度で丸めた値を返します。ndigits が省略されたり、
None
だった場合、入力値に最も近い整数を返します。
とあります。今回は round(2.5)
のように、引数を一つだけ指定しているので、「ndigits が省略された」場合にあたります。よって、「入力値に最も近い整数を返す」はずです。
では、2.5 に最も近い整数とは何でしょうか?
……そうです、2 と 3 の二つがありますね。
このような場合はどうなるでしょうか? 続きを読んでみましょう。ndigits が登場する部分は今回は関係なさそうなので飛ばします。次の部分が怪しそうですね。
二つの倍数が同じだけ近いなら、偶数を選ぶ方に (そのため、例えば
round(0.5)
とround(-0.5)
は両方とも0
に、round(1.5)
は2
に) 丸められます。
なんと、2 つの倍数が同じだけ近いなら、偶数が選ばれるようです。
だから、round(3.5)
は 3 ではなく 4 に、round(2.5)
は 3 ではなく 2 になったのですね。
しかし、なぜこのような面倒な仕組みになっているのでしょうか?
なぜこのような仕組みがあるのか
このような仕組みを「偶数丸め」「銀行丸め」などというようです。(丸めというのは四捨五入や切り上げ・切り捨てのように、端数を処理することを指します)
なぜこのような仕組みがあるのか考えてみましょう。
例えとして、釣った魚か何かの合計の重量を計算することを考えます。2.1kg, 3.6kg, 1.8kg, 3.2kg, …. などとたくさんの魚を釣って、それを合計したいということです。
まず、これら全ての魚の重量を「切り上げしてから」足したら、当然合計の重量は増えます。同様に、「切り捨てしてから」足したら、合計の重量は減りますね。
では、切り上げや切り捨てではなく、四捨五入したらどうでしょうか。
四捨五入の場合は、増えたり減ったりの偏りは生じない……ような気がしますよね。
でも、ここでよく考えてみましょう。
四捨五入の場合、
- 2.0 は 2 になる (-0.0)
- 2.1 は 2 になる (-0.1)
- 2.2 は 2 になる (-0.2)
- 2.3 は 2 になる (-0.3)
- 2.4 は 2 になる (-0.4)
- 2.5 は 3 になる (+0.5)
- 2.6 は 3 になる (+0.4)
- 2.7 は 3 になる (+0.3)
- 2.8 は 3 になる (+0.2)
- 2.9 は 3 になる (+0.1)
です。() 内は、元の数からどれだけ変わったかを表しています。
よって、小数第 1 位の 0 から 9 が均等に出てくると考えた際、
-0.0 + (-0.1) + (-0.2) + (-0.3) + (-0.4) + 0.5 + 0.4 + 0.3 + 0.2 + 0.1 = 0.5
となって、+0.5 だけ、増える側に偏ってしまうということになります。
では、これを「偶数丸め」でやってみるとどうなるでしょうか。
偶数丸めの場合は、2.5 が 3 になるのではなく 2 になるのでした。よって、2.5 の元の数からの変化は (+0.5) ではなく (-0.5) となります。
- 2.0 は 2 になる (-0.0)
- 2.1 は 2 になる (-0.1)
- 2.2 は 2 になる (-0.2)
- 2.3 は 2 になる (-0.3)
- 2.4 は 2 になる (-0.4)
- 2.5 は 2 になる (-0.5)
- 2.6 は 3 になる (+0.4)
- 2.7 は 3 になる (+0.3)
- 2.8 は 3 になる (+0.2)
- 2.9 は 3 になる (+0.1)
すると、
-0.0 + (-0.1) + (-0.2) + (-0.3) + (-0.4) + (-0.5) + 0.4 + 0.3 + 0.2 + 0.1 = -0.5
となり、-0.5 だけ減る側に偏りました。
……これを見ただけでは何も解決していないように思えますが、続きについて考えましょう。
この先、◯.5 の数についての偶数丸めは、
- 3.5 は 4 になる (+0.5)
- 4.5 は 4 になる (-0.5)
- 5.5 は 6 になる (+0.5)
- 6.5 は 6 になる (-0.5)
- ……
と続いていきます。
よって、3.◯ の数の合計の偏りは +0.5 に、4.◯ の数の合計の偏りは -0.5 に、……と交互に、「増える側への偏り」と「減る側への偏り」が続いていくことになります。
そのため、整数部分の偶数・奇数の偏りがないのであれば、増える側への偏りと減る側への偏りが相殺されることになるのです。
このような理由から、四捨五入の代わりに「偶数丸め」を使うことがあると考えられます。
考えてみれば、「偶数丸め」というのは、整数部分が奇数の場合には「四捨五入」をし、整数部分が偶数の場合には「五捨六入」をしているようなものですね。