【Unity】Mathf.Round()メソッドの挙動の注意点

こじゃらこじゃら

次のような値を丸める処理を書こうとしているんだけど…

Debug.Log(Mathf.Round(-2.5f));
Debug.Log(Mathf.Round(-1.5f));
Debug.Log(Mathf.Round(-0.5f));
Debug.Log(Mathf.Round(0.5f));
Debug.Log(Mathf.Round(1.5f));
Debug.Log(Mathf.Round(2.5f));
こじゃらこじゃら

正しく四捨五入されないみたいなの…

-2
-2
0
0
2
2
このはこのは

Mathf.Round()は四捨五入ではなく偶数丸めを行っているからなの。

本記事では、Mathf.Round()メソッドの使用上における注意点について解説していきます。

スポンサーリンク

Mathf.Round()メソッドとは

与えられた値に最も近い整数を返すメソッドです。
次の形式で定義されています。

メソッドの定義

public static float Round(float f);

参考:Mathf-Round – Unity スクリプトリファレンス

ただし、端数が.5の場合は最も近い偶数を返します。

これは偶数丸め と呼ばれています。
四捨五入ではないことに注意する必要があります。

メソッドの内部実装

Mathf.Round()メソッドは次のようにMath.Round()メソッドをラップして実装されています。

(float)Math.Round((double)f);

Mathクラスは.NET側で提供されている数学関連のクラスです。
一方でMathf構造体はUnity側が提供している数学関連の構造体です。

Unityではdouble型よりfloat型のほうがよく使われるため、特別にパフォーマンスを気にしない限り、通常はfloat型を扱いやすいMathf構造体を使うので問題ありません。

偶数丸めが採用される理由

Mathf.Round()で採用される偶数丸めは、丸めた値を足し合わせたときに四捨五入よりも誤差が小さくなりやすいという利点があります。

偶数丸め・四捨五入の合計値の誤差の比較

次の値の合計値を求めることを例にとってみましょう。

0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 8.5, 9.5, 10.5

これらの値をそのまま足し合わせた場合、偶数丸め四捨五入してから足し合わせた場合でそれぞれ比較してみましょう。

合計値は以下のようになります。

元の値偶数丸め四捨五入
合計値60.56066

結果を見ると、偶数丸めの合計値は誤差が0.5であるのに対し、四捨五入の合計値は5.5まで増えていることが分かります。

このように、端数が.5の値が沢山出てくる場合は、四捨五入より偶数丸めのほうが誤差の蓄積が少なくなりやすい性質があります。

ただし、元の値に0.5足した値の分布が偶数または奇数に偏っている場合はこの限りではありません。

四捨五入の実現方法

どうしても四捨五入で丸めたい場合は、次の.NET標準のMath.Round()メソッドが使えます。

public static double Round(double value, int digits, MidpointRounding mode);

第1引数valueには丸め対象の値、第2引数digitsには戻り値の小数部の桁数、第3引数modeには端数が.5の場合の丸め方を指定します。

四捨五入で値を丸めて整数にしたい場合、第2引数digitsには0第3引数modeにはMidpointRounding.AwayFromZeroを指定します。

MidpointRounding.AwayFromZeroは数値が2つの中間に位置するとき、ゼロから遠い方の最も近い数値に丸めるモードです。

実際の使用例と出力結果は次のようになります。

使用例

Debug.Log(Math.Round(-2.5f, 0, MidpointRounding.AwayFromZero));
Debug.Log(Math.Round(-1.5f, 0, MidpointRounding.AwayFromZero));
Debug.Log(Math.Round(-0.5f, 0, MidpointRounding.AwayFromZero));
Debug.Log(Math.Round(0.5f, 0, MidpointRounding.AwayFromZero));
Debug.Log(Math.Round(1.5f, 0, MidpointRounding.AwayFromZero));
Debug.Log(Math.Round(2.5f, 0, MidpointRounding.AwayFromZero));

出力結果

入力値が負の場合は、負の方向(ゼロから遠い方向)に丸められます。

さいごに

Mathf.Round()とMath.Round()で採用されている偶数丸めは、ほかの言語でも多く採用されている方法です。

一方で、Excelなど四捨五入を採用しているソフトウェアも存在します。

事前にどの丸め方を採用しているかを把握しておくと安心でしょう。

参考サイト

スポンサーリンク