整数に丸める時の注意

Pythonだと

>>> round(1.5)
2.0
>>> round(2.5)
3.0
>>> round(3.5)
4.0
>>> round(4.5)
5.0

Rだと

> round(1.5)
[1] 2
> round(2.5)
[1] 2
> round(3.5)
[1] 4
> round(4.5)
[1] 4

round(偶数.5)の結果は実装によって異なるらしい。端数処理 - Wikipediaによれば、整数に丸めるには少なくとも2つの方式があり、算数で習った「四捨五入」と、元の値に近い偶数側に丸める「最近接偶数への丸め」が含まれる。

四捨五入だと整数間のちょうど真ん中の数値であるX.5が常に正側に丸められることで、丸めた値の総和が下の値の総和よりずっと大きくなる一方で、最近接偶数への丸めはX(偶数).5の場合負側に丸められるため、全体としてバイアスが小さくなるという性質がある。従って、最近接偶数への丸めは、四捨五入よりも、丸めた値の総和が元の値の総和に近くなるため、金融分野や統計分野で好まれ、IEEE 754やISO/JISなどで標準の丸め方式として採用されている。

Cだと、round() が四捨五入で、 rint() が最近接偶数への丸めである。どのくらい差が出るものなのか、試してみた。


$ gcc -lm test_rint_round.c
$ for i in $(jot 5); do ./a.out; sleep 1; done
== result ==
sum(x)        = 498305.00
sum(rint(x))  = 498296.00 (diff: -9.00)
sum(round(x)) = 498805.00 (diff: 500.00)
== result ==
sum(x)        = 505284.00
sum(rint(x))  = 505292.00 (diff: 8.00)
sum(round(x)) = 505784.00 (diff: 500.00)
== result ==
sum(x)        = 504254.00
sum(rint(x))  = 504260.00 (diff: 6.00)
sum(round(x)) = 504754.00 (diff: 500.00)
== result ==
sum(x)        = 500216.00
sum(rint(x))  = 500226.00 (diff: 10.00)
sum(round(x)) = 500716.00 (diff: 500.00)
== result ==
sum(x)        = 513174.00
sum(rint(x))  = 513164.00 (diff: -10.00)
sum(round(x)) = 513674.00 (diff: 500.00)

多くの実装に、round()という関数があるが、いつも四捨五入とは限らない。使う前に、また、説明する前に確認しておく癖を付けた方が良いだろう。