2010-07-08 79 views
7

這是我在做什麼,它的工作99.999%的時間:將double值乘以100.0會引入舍入誤差?

((int)(customerBatch.Amount * 100.0)).ToString() 

量的值是一個雙。我正在嘗試將這些值以便士的形式寫入文本文件,以便傳輸到服務器進行處理。金額永遠不會超過2位數的精度。

如果您使用金額580.55,這行代碼返回58054作爲字符串值。

此代碼在64位的Web服務器上運行。

任何想法?

+0

浮點處理錯誤:http://stackoverflow.com/questions/2248748/dealing-with-floating-point-errors-in-net – Greg 2010-07-08 15:07:33

+3

見本文章:http://docs.sun.com/source/806-3568/ncg_goldberg。html TL; DR:不要使用浮點數或雙精度計算 – Piskvor 2010-07-08 15:09:16

+0

該值來自Money值數據庫形式的SQL Server數據庫。對象包裝類對這些值使用'double',所以我無法更改Amount的類型。 – Sophtware 2010-07-08 15:11:58

回答

21

你應該真的使用十進制計算貨幣。

((int)(580.55m * 100.0m)).ToString().Dump(); 
+0

+1,文檔甚至建議使用小數進行財務/貨幣計算:http://msdn.microsoft.com/en-us/library/364x0z75.aspx – 2010-07-08 15:10:18

+0

忽略.Dump()方法。從LinqPad複製和粘貼。 – 2010-07-08 15:12:36

+1

所以你建議使用: ((int)((十進制)customerBatch.Amount * 100.0m))。ToString() – Sophtware 2010-07-08 15:15:52

8

您可以使用decimal值進行精確計算。 Double是浮點數,在計算過程中不能保證精確。

0

我的建議是將該值存儲爲便士的整數值,並採取dollars_part = pennies/100cents_part = pennies % 100。這將完全避免舍入錯誤。

編輯:當我寫這篇文章,我沒有看到你不能改變數字格式。最好的答案可能是像其他人所建議的那樣使用循環方法。

編輯2:正如其他人指出的那樣,最好使用某種定點十進制變量。這比我原來的解決方案要好,因爲它會將有關小數點位置的信息存儲在它所屬的值中而不是代碼中。

4

我在猜測580.55正在轉換爲58054.99999999999999999999999999 ...在這種情況下,int會將其舍入到58054.您可能想要編寫自己的函數,將您的金額通過某種舍入轉換爲int或閾值以使這不會發生。

+0

如果是這樣,int取整數部分,而不是舍入。 ((int)(round(customerBatch.Amount * 100.0)))。ToString()呢? – Alejandro 2010-07-08 15:15:03

+0

是的,這可能會工作(我不熟悉C#,並不知道它有一個內置的循環函數)。 – 2010-07-08 15:16:51

+0

你瘋狂猜測。 58054.9999999 ...不能精確地表示爲一個浮點數比580.55更準確。 – 2010-07-08 15:17:02

0

由於這樣的舍入誤差,您實際上不應該使用雙值來表示貨幣。

相反,您可能會考慮使用積分值來表示貨幣金額,以便它們完全表示。要表示小數點,您可以使用將580.55作爲值58055的類似技巧。

0

不,乘法不會引入舍入誤差 但不是所有的值都可以用浮點數表示。 x.55是其中之一)

+2

換句話說,在計算機上乘以浮點數*會引入舍入錯誤。 – Piskvor 2010-07-08 15:12:48

+1

580.55以580.5499999999xxx作爲「雙精度」存儲時發生第一個舍入誤差。乘以100得到58054.99999999xxx,這實際上並不比580.5499999999xxx準確。 – supercat 2010-07-08 16:16:33

+0

當然,浮點類型的乘法會引入舍入錯誤。乘以兩個N位尾數需要大約2N位來精確地存儲產品。您必須丟失大約N位才能將結果再次存儲回相同的類型。 – sigfpe 2010-07-08 16:36:40

3

嘗試

((int)(Math.Round(customerBatch.Amount * 100.0))).ToString()