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特有の誤差、銀行型の丸めという３つの要素があるとのことで、406を期待した理由から考え直して予想してみました。  
MS-DOSの時代にコンパイルされたEXEの計算挙動を調べていたというのが「C#の四捨五入がなぜ違うのか」と思った原因でもありました。  
そのEXEは数値の扱いがdouble型の挙動でしたが計算式の結果値を64ビットよりも少ない有効桁数に丸める特徴（仕様）があったので、計算式中と結果値の桁数をそれぞれ調べていたところ
SQRT(99999)が316.22618487406
と表示されるので丸め方を予測していたところ、どうしても11桁での四捨五入で最後の4055の5が繰り上がっているとしか思えなかったのです。

丸め方がCPUにdoubleを扱わせるのとは関係ないのであれば、当時は丸め方の方針そのものがコンパイラメーカー又はEXEを作った人の独自実装だったのかもしれないので、IEEE754や銀行型丸めでもなく、一般的な四捨五入となるように実装されていたのかもしれません。（double型特有の誤差を無くしての四捨五入を実装する方が、逆に大変そうですが・・・）




10進数の316.226184874055は、2進数では正確に表現できません。最も近い倍精度浮動小数点数を選ぶと、それは316.226184874055よりわずかに小さな数になります(*1)。従って、11桁目での丸めとしては316.22618487405の方が316.22618487406より近いのです。

もうすこし細かい話としては、「2進数の数値を10進数のx桁で丸め、結果を2進数の数値で得る」というのは厳密な意味での「丸め」でありません。結果の桁数が減らないからです。10進数での丸めが必要ならば、最初から10進数で扱い、結果も10進数で得る必要があります
(BLUEPIXYさんの回答のようにdecimal型を使うのはその例となります。) 詳しくは次の記事をどうぞ。

<http://blog.practical-scheme.net/shiro/20131229-flonum-rounding>

(*1) 316.226184874055に最も近い倍精度浮動小数点数の値は 695388734552533/2199023255552
で、これは10進数での正確な値 316226184874055/1000000000000 より小さい

