

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型特有の誤差を無くしての四捨五入を実装する方が、逆に大変そうですが・・・）


CC BY-SA 3.0
2016-01-04T11:03:29.670
20589
2016-01-05T01:57:12.007
2016-01-05T01:57:12.007
13808
13808
question
c# .net
C#のMath.Roundで小数の四捨五入がおかしいケース


`double`
の表現精度は１５桁で、`316.226184874055`は１５桁なので保持する数値を内部的に(内部的には１７桁ある)正しく表現できていないのではないかと思います。  
このような場合(数の表現桁数が限度に近い場合、浮動小数の表現に正確性を求められる場合)には、decimal型を使うのがいいかと思います。

例：  
`Math.Round(316.226184874055m, 11);`//=>316.22618487406


CC BY-SA 3.0
2016-01-04T11:29:52.357
20591
2016-01-04T11:29:52.357
5044
20589
answer


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 より小さい


CC BY-SA 3.0
2016-01-04T11:34:19.613
20592
2016-01-04T11:44:53.130
2016-01-04T11:44:53.130
5258
5258
20589
answer


質問にある`316.226184874055`に関してはshirokさんの回答通りですが、その他に[Math.Round()](https://msdn.microsoft.com/ja-
jp/library/75ks3aby\(v=vs.100\).aspx)は正確には四捨五入ではなく銀行型丸めです。解説にあるように`2.135`は`2.13`へ切り捨てられます。四捨五入を行うには同解説の

> 丸めの種類を制御するには、Math.Round(Double, Int32, MidpointRounding) オーバーロードを呼び出します。

に従う必要があります。

* * *

shirokさんの書かれた値をWindowsの電卓で計算してみました。  
69538873455253 **3** /2199023255552 = 316.2261848740 **549** 7944564558565617  
69538873455253 **4** /2199023255552 = 316.2261848740 **554** 3419299647212029  
ということで`316.226184874055`に最も近い値は前者ですね。


CC BY-SA 3.0
2016-01-04T12:10:15.107
20595
2016-01-04T13:58:45.933
2016-01-04T13:58:45.933
4236
4236
20589
answer


浮動小数点数の内部表現誤差によるものです。C#に限らず多くの言語で再現すると思われます。(多くの環境で[IEEE
754](https://ja.wikipedia.org/wiki/IEEE_754)という標準規格に準拠した内部表現が使われているためです。)

なお、小数部桁数指定版のMath.Round(double value, int
digit)の[実装](http://referencesource.microsoft.com/#mscorlib/system/math.cs)では、value
* 10digitを整数に丸め、10digitで割る、という処理を行っています。

そして、今回の数値の実際の内部値が正確にどうなっているか追いかけてみます。  
(実数の10進表現→浮動小数2進表現は一般に正確に変換できませんが、浮動小数2進表現→10進表現は厳密に正確に行うことが可能です。今回は[Jon
Skeet](https://stackoverflow.com/users/22656/jon-
skeet)氏の[DoubleConverter](http://jonskeet.uk/csharp/DoubleConverter.cs)を使用させていただきました。)

矢印の右側が正確な値です。  
316.226184874055 → 316.22618487405497944564558565616607666015625  
316.22618487405497944564558565616607666015625 * 1011 → 31622618487405.49609375  
これを丸めると 31622618487405。内部表現誤差による影響が出ていることがわかります。

もう少し進めると、  
31622618487405 / 1011 → 316.2261848740499772247858345508575439453125  
つまり厳密には`Math.Round(316.226184874055d,
11);`の結果は316.2261848740499772247858345508575439453125です。  
これをdouble.ToString()などで普通に文字列化すると15桁に丸められ、316.226184874050。  
最後の0を省略して316.22618487405となります。

[確認用 .NET Fiddle](https://dotnetfiddle.net/Inkug4)


CC BY-SA 3.0
2016-01-04T15:27:11.150
20604
2016-01-04T15:27:11.150
2017-05-23T12:38:55.307
-1
4765
20589
answer