Math.Round(8.075, 2, MidpointRounding.AwayFromZero)
返回8.07
,雖然它應該返回8.08
。神祕地,7.075
工作正常,但9.075
也返回9.07
!Math.round bug - 該怎麼辦?
怎麼辦?有沒有人知道沒有這種錯誤的舍入方法?
Math.Round(8.075, 2, MidpointRounding.AwayFromZero)
返回8.07
,雖然它應該返回8.08
。神祕地,7.075
工作正常,但9.075
也返回9.07
!Math.round bug - 該怎麼辦?
怎麼辦?有沒有人知道沒有這種錯誤的舍入方法?
我不是一個.NET專家,但這些數字不能準確地表示爲雙,所以如果你考慮到那些3個數字的真正價值四捨五入精確:
7.075 ==> 7.07500000000000017763568394002504646778106689453125
8.075 ==> 8.074999999999999289457264239899814128875732421875
9.075 ==> 9.074999999999999289457264239899814128875732421875
更多關於浮點精度:What Every Computer Scientist Should Know About Floating-Point Arithmetic。
你是如何找到真正的價值的? –
@AshBurlaczenko用java程序,但我想你可以在.NET中做同樣的事情。 – assylias
@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/ –
如果用10個指頭算,像人類這樣做,你不要有任何麻煩表示十進制值8.075正是:
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 + ...
我放棄了用手指抽筋鍵入條件,但問題是,無論第2多強你怎麼加,你就永遠不會得到準確8.075米。一個類似的問題是人類無法精確地寫出10/3的結果,它在分數中有無限數量的數字。用6個手指數時,只能正確地寫出該表達式的結果。
處理器當然沒有足夠的存儲來存儲無限數量的位來表示值。所以他們必須截斷數字序列,double類型的值可以存儲53位。
因此,當存儲在處理器中時,十進制值8.075會變爲四捨五入。轉換回十進制的53位序列的值爲〜8.074999999999999289。然後,如預期的那樣,通過您的代碼四捨五入到8.07。
如果您需要10個手指數學結果,則需要使用以10爲基數存儲數字的數據類型。這就是.NET中的System.Decimal類型。修復:
decimal result = Math.Round(8.075m, 2, MidpointRounding.AwayFromZero)
請注意字母m在8.075m文字中的用法,該文字是片段中的一個十進制的文字。它選擇用10個手指計數的Math.Round()超載,之前您使用的是使用System.Double(2指版本)的超載。
請注意,使用System.Decimal計算存在明顯的缺點,它是慢。比使用System.Double計算速度慢得多,這是處理器直接支持的值類型。十進制數學是用軟件完成的,並不是硬件加速的。
這就是爲什麼*真正*數學家[使用60指數](http://en.wikipedia.org/wiki /六十進制)。 – svick
可能的解決方案可能是:
(double)Math.Round((decimal)8.075, 2, MidpointRounding.AwayFromZero);
我很懷疑.NET都會有這樣的錯誤,如果是什麼結果,那麼我敢肯定,這是實現這種方式的一個原因。 –
您可以使用小數類型,因爲它們更精確:'Math.Round(8.075m,2,MidpointRounding。AwayFromZero)' –
我們需要Jon Skeet在這裏 – VladL