C#のMath.Roundで小数の四捨五入がおかしいケース
Math.Round(316.226184874055d, 11);
としたとき、 316.22618487405 が返ってきます。
11桁を指定したので、12桁の5が繰り上がることで、最後が 406 となるのを期待していたのですが、切り捨てのような動作でした。
C#ではこのようになってしまうケースがある、という感じなのでしょうか?
みなさん、ありがとうございます。
丸める直前まではdouble特有の誤差が必要だったので、丸め処理だけ一旦、
Math.Round((decimal)doubleの変数);として、返値をdouble型にすることで解決しました。
double test1 = 316.226184874055d;
double test2 = (double)Math.Round((decimal)test1, 11, MidpointRounding.AwayFromZero);
※test2の値はdouble型で316.22618487406になりました。
IEEE754準拠とdouble特有の誤差、銀行型の丸めという3つの要素があるとのことで、406を期待した理由から考え直して予想してみました。
MS-DOSの時代にコンパイルされたEXEの計算挙動を調べていたというのが「C#の四捨五入がなぜ違うのか」と思った原因でもありました。
そのEXEは数値の扱いがdouble型の挙動でしたが計算式の結果値を64ビットよりも少ない有効桁数に丸める特徴(仕様)があったので、計算式中と結果値の桁数をそれぞれ調べていたところ SQRT(99999)が316.22618487406 と表示されるので丸め方を予測していたところ、どうしても11桁での四捨五入で最後の4055の5が繰り上がっているとしか思えなかったのです。
丸め方がCPUにdoubleを扱わせるのとは関係ないのであれば、当時は丸め方の方針そのものがコンパイラメーカー又はEXEを作った人の独自実装だったのかもしれないので、IEEE754や銀行型丸めでもなく、一般的な四捨五入となるように実装されていたのかもしれません。(double型特有の誤差を無くしての四捨五入を実装する方が、逆に大変そうですが・・・)