C#で、これまで四捨五入というと何となく
double y = (int)(x + 0.5);
とか、
double y = System.Math.Round(x);
と、行っていたのですが、画像処理で補間処理を行う時に、座標を四捨五入しようとすると、実は自分の思う通りに動いていなかった事に今更ながらに気が付いた...
何はともあれ、int、Roundを使った方法にプラスして、Floorを使って、このようなコード↓
int i;
double x;
double y_int, y_Round, y_Floor;
for (i = -30; i <= 30; i++)
{
x = i * 0.1;
y_int = (int)(x + 0.5);
y_Round = System.Math.Round(x);
y_Floor = System.Math.Floor(x + 0.5);
System.Diagnostics.Debug.WriteLine(
x.ToString() + ", " +
y_int.ToString() + ", " +
y_Round.ToString() + ", " +
y_Floor.ToString()
);
}
を実行すると、結果は下図のようになります。
これを見ると、intを使った方法では、-1.4~+0.4までが0となり、マイナス側の結果がいまいち。
Roundを使った方法では、一見良さそうなのですが、よ~く結果を見てみると、
0.5は0に、1.5は2になっている。
これは銀行型丸め(偶数丸め)と言うらしく、整数の間の値は、最も近い整数に丸めこまれるが、距離が同じ場合は偶数の値を返すとのこと。
(参考)
Math.Round メソッド
http://msdn.microsoft.com/ja-jp/library/wyk4d9cy.aspx
そんな事、全く知らなかった~
という事で、結局はFloorを使うと、小数の値が10個ずつ整数へ変換されているので、画像の座標に関する四捨五入では、ムラなく整数へ変換されているFloorを使うのが良さそうです。-0.5⇒0, -1.5⇒-1へと変換される部分が、ちょっと気持ち悪いですが。
(追記)
コメントにもあるように、数学的には四捨五入は
double y = System.Math.Round(x, MidpointRounding.AwayFromZero);
とすると良いと思います。
ここでの記事は、あくまで、座標を丸める場合のお話です。
コメント
こんにちは。
double y = System.Math.Round(x, MidpointRounding.AwayFromZero);
が一番しっくりくるよう気がします・・・
こんばんは。
確かに普通の四捨五入ならAwayFromZeroを使うのがいいでしょうね。
とか言いながら、この記事を書くのにいろいろ調べてて、今回、初めてAwayFromZeroの事を知りましたw
画像処理をやっていると、座標に関して四捨五入をしたい場合が良くあるかと思いますが、AwayFromZeroを使うとー0.5を四捨五入するとー1、+0.5を四捨五入すると+1になりますよね?
するとー0.5~+0.5の範囲内だけ0になる範囲が他の値よりも若干狭くなるのが気持ち悪くて、座標を四捨五入する場合はFloorの方がしっくりくるという意味合いでした...