四捨五入と偶数丸め (PHP8.4以降)
PHP8.4で、round()関数に丸め方のオプションが追加されました。
端数がちょうど5だった場合の処理が選べるようになります。オプションは
- RoundingMode::HalfAwayFromZero : 0から離れる方向へ丸める
- RoundingMode::HalfTowardsZero : 0に近づく方向へ丸める
- RoundingMode::HalfEven : 偶数側に丸める
- RoundingMode::HalfOdd : 奇数側に丸める
です。未指定だった場合の処理はRoundingMode::HalfAwayFromZeroです。
偶数丸め(銀行丸め)
単純な四捨五入だと、端数5を切り上げるため、データ全体としては正の方向へわずかにずれます。一方で、偶数丸めだと、端数5は切り上げる場合と切り捨てる場合があり、データ全体の誤差は抑えられます。偶数丸めは銀行丸めとも呼ぶらしいです。
実際に実行してみた
以下のようなコードを実行してみました。
// creates a sample $data 1 - 99
$data = [];
for($i = 1; $i < 100; $i++){
$data[] = $i ;
}
$resultRaw = 0;
$resultHalfAwayFromZero = 0;
$resultHalfEven = 0;
// pickups randomly from $data
$times = 10000;
for($i = 0; $i < $times; $i++){
shuffle($data);
$resultRaw += $data[0];
$resultHalfAwayFromZero += round($data[0],-1, RoundingMode::HalfAwayFromZero);
$resultHalfEven += round($data[0],-1, RoundingMode::HalfEven);
}
echo '元データ: ' . $resultRaw . '<br>' . PHP_EOL;
echo '四捨五入: ' . $resultHalfAwayFromZero . '<br>' . PHP_EOL;
echo '偶数丸め: ' . $resultHalfEven . '<br>' . PHP_EOL;
実行結果は↓ のようになりました。
元データ: 499984
四捨五入: 504890
偶数丸め: 499910
偶数丸めのほうが、元データとの差が少なかったです。
既存システムの変更は必要か?
偶数丸めのほうが誤差が少ないようです。とはいえ、では偶数丸めを採用すればよいか、というと、新規プロダクトなら採用すればよいでしょう。一方で既存システムでは偶数丸めに変更する必要性は低いと思われます。
既存システムが四捨五入だった場合には、もちろん誤算の観点からいえばより良くなります。しかし、いままで四捨五入が受け入れられていたのであれば、四捨五入による誤差も許容されていたわけです。あえて変更しなくても良さそうに思います。


