2013-05-18 8 views
6

Math.Round(8.075, 2, MidpointRounding.AwayFromZero)はを返す必要がありますが、8.07を返します。不思議なことに、7.075は正常に動作していますが、9.0759.07を返します。Math.roundバグ - どうすればよいですか?

どうすればよいですか?誰もそのようなバグのない丸め方法を知っていますか?

+2

それは私はそれが理由でそのように実装されたと確信して、次に起こるのあるものであれば、私は、.NETは、このようなバグを持つことになり非常に疑わしいです。 –

+2

より正確な十進数型を使用することができます: 'Math.Round(8.075m、2、MidpointRounding。AwayFromZero) ' –

+0

ここでJon Skeetが必要です – VladL

答えて

4

私は、.NETの専門家ではないけど、これらの数字は正確に二重のように表すことができないので、あなたのアカウントにこれらの3つの数字の真の値を取る場合は、丸めが正確である:

7.075 ==> 7.07500000000000017763568394002504646778106689453125 
8.075 ==> 8.074999999999999289457264239899814128875732421875 
9.075 ==> 9.074999999999999289457264239899814128875732421875 

詳細浮動小数点精度について:What Every Computer Scientist Should Know About Floating-Point Arithmetic

+0

どのようにして実際の価値観を見つけましたか? –

+0

@AshBurlaczenko Javaプログラムでは、.NETで同じことができると思います。 – assylias

+0

@AshBurlaczenko:私はC#プログラムをここに持っています:http://blogs.msdn.com/b/ericlippert/archive/2011/02/17/looking-inside-a-double.aspxと最近のフォローアップここに投稿してください:http://ericlippert.com/2013/05/13/spot-the-defect-rounding/ –

9

あなたは人間が行うように、あなたはどんなトラブル正確小数点値8.075を表現していない、10本の指で数える場合:

8.075 = 8 x 10^1 + 0 x 10^0 + 7 x 10^-1 + 5 x 10^-2 

しかし、コンピュータは2本の指で数え、彼らは中にその値を表現する必要があります2のべき乗:

8.075 = 1 x 2^3 + 0 x 2^2 + 0 x 2^1 + 0 x 2^0 + 0 x 2^-1 + 0 x 2^-2 + 0 x 2^-3 + 
      1 x 2^-4 + 0 x 2^-5 + 0 x 2^-6 + 1 x 2^-7 + 1 x 2^-8 + 0 x 2^-9 + 0 x 2^-10 + 
      1 x 2^-11 + ... 

あなたは正確に8.075メートルを取得し、決して私は用語を入力して指のけいれんをあきらめなかったが、ポイントは、追加どのように多くの2のべき乗という問題です。人間が10/3の結果を決して書くことができないような同様の問題は、端数に無限の桁数を持ちます。 6本の指で数えた場合にのみ、その式の結果を正確に書くことができます。

もちろんプロセッサには、値を表すために無限のビット数を格納するのに十分な記憶域がありません。したがって、の数字シーケンスを切り捨てる必要があります.2倍の値には53ビットを格納できます。

結果として、小数点値8.075はプロセッサに格納されるときに丸められます。 10進数に変換された53ビットのシーケンスは、値〜8.074999999999999289です。予想どおり、あなたのコードによって8.07に丸められます。

10指の計算結果が必要な場合は、数値をベース10に格納するデータ型を使用する必要があります。これは.NETのSystem.Decimal型です。 [FIX

decimal result = Math.Round(8.075m, 2, MidpointRounding.AwayFromZero) 

注、スニペットにリテラル型小数のリテラル8.075メートルの文字mの使用。以前は、2本の指のバージョンであるSystem.Doubleを使用するオーバーロードを使用していましたが、これは10本の指で数えられるMath.Round()オーバーロードを選択します。

System.Decimalで計算することに大きな欠点があることに注意してください。です。 System.Doubleで計算するよりもずっと遅く、プロセッサで直接サポートされている値の型です。小数点演算はソフトウェアで行われ、ハードウェアアクセラレーションは行われません。

+1

そのため、*本物の数学者[60本の指を使って数える](http://en.wikipedia.org/wiki)/Sexagesimal)。 – svick

-2

可能な解決策は次のようになります。

(double)Math.Round((decimal)8.075, 2, MidpointRounding.AwayFromZero); 
関連する問題