2012-04-12 31 views
37

我一直在爭取C#中的十進制精度來自一個SQL十進制(38,30),並且我終於把它變成了一個四捨五入的奇怪。我知道我可能忽略了這裏的明顯,但我需要一點洞察力。是C#十進制舍入不一致嗎?

我遇到的問題是,C#不會產生我認爲是一致的輸出。

decimal a = 0.387518769125m; 
decimal b = 0.3875187691250002636113061835m; 

Console.WriteLine(Math.Round(a, 11)); 
Console.WriteLine(Math.Round(b, 11)); 
Console.WriteLine(Math.Round(a, 11) == Math.Round(b, 11)); 

息率

0.38751876912 
0.38751876913 
False 

呃,0.38751876913?真?我在這裏錯過了什麼?

MSDN

如果小數位置中的數字爲奇數,它被改變成偶數位。否則,保持不變。

爲什麼我看到不一致的結果?額外的精度不會改變「在小數位數字」 ......

+4

它向下舍入的值<= 0.5,> 0.5的值。 (即「0.50000」被取整,「0.50002 ...」被取整) - 這不是你期望的嗎?請參閱http://msdn.microsoft.com/en-us/library/ms131274.aspx – 2012-04-12 16:13:36

+0

@Jason Williams:不完全。如果小於0.5,則向下舍入,如果> 0.5,則向上舍入,然後如果== 0.5則使用銀行家舍入。 – jason 2012-04-12 16:21:58

+18

您期待的圓形函數可以舍入*更遠的數字*?你能解釋爲什麼你會期望在任何情況下? (這不是一個反問題的問題,我有興趣瞭解爲什麼人們會相信很奇怪的東西,這個問題其實很常見,但我仍然不明白爲什麼那麼多人認爲* round *應該圍繞* number更遠*) – 2012-04-12 16:35:18

回答

42

從MSDN:

如果存在單個非零數字在d向右側decimals小數點位置並且它的值是5,在小數位置中的數字被四捨五入如果它是奇數,或者保持不變,如果它甚至是。如果d的小數位數少於decimals,則d將保持不變。

在你第一種情況下

decimal a = 0.387518769125m; 
Console.WriteLine(Math.Round(a, 11)); 

是一個數字到第11位的權而且這個數字是5。因此,由於位置11是偶數,因此保持不變。因此,你會得到

0.38751876912 

在你的第二個案例

decimal b = 0.3875187691250002636113061835m; 
Console.WriteLine(Math.Round(b, 11)); 

不是一個單一的數字到第11位的權。因此,這是小學四捨五入的直線;如果下一個數字大於4,則向上取整,否則向下取整。由於數字到第11位的右邊是4次以上(這是一個5),我們圓了,所以你看到

0.38751876913 

爲什麼我看到的結果不一致?

你不是。結果與文檔完全一致。

+2

只是爲了確保我在這裏能夠理解,ToEven或AwayFromZero會根據數字的精度而改變默認值? – 2012-04-12 16:48:06

+0

我喜歡你的答案,我認爲它顯示了MSDN文檔中缺少的東西,也就是說,如果它不是一個非零數字 – 2012-04-12 17:42:30

+4

@BennettDill「單個非零數字等於5」是一種混淆的方式,表示「方法的參數恰好在兩個可能的返回值之間的一半」。如果*沒有*右邊的一個數字指定的地方,或者如果只有一個不是5的數字,那麼參數並不完全在可能的返回值之間,所以你選擇了更接近參數的返回值。啊,如果我們只使用了基數3數字,我們不必擔心這個! – phoog 2012-04-12 18:56:54

26

從MSDN - Math.Round Method (Decimal, Int32)

如果有一個非零數字在d到小數的權小數點位置,其值爲5,小數點位置的數字爲奇數時爲四捨五入,如果爲偶數則保持不變。如果d的小數位數小於小數位數,則d不變。

此方法的行爲遵循IEEE標準754第4部分。這種舍入有時稱爲四捨五入到舍入,或銀行家舍入。它最大限度地減少了在一個方向上始終舍入中點值所導致的舍入誤差。

注意使用單個非零數字。這對應你的第一個例子,但是不是第二個。

和:

爲了控制舍入所使用的輪(十進制,Int32)方法的類型,調用Decimal.Round(十進制,的Int32,MidpointRounding)過載。

+0

我不認爲我的問題是使用哪種類型的舍入。我相信它不理解MidpointRounding.ToEven本身的行爲。 :-(雖然這個問題的答案和評論已經明確地說明了,但是要感謝所有人! – 2012-04-12 20:39:48

4

該部分「小數點右側小數點後面的單個非零數位,其值爲5」說明結果。只有當輪的部分恰好爲0,5時,舍入規則纔會起作用。

+0

從MSDN的文檔中,我不清楚這一點。感謝您的解釋。 – 2012-04-12 17:45:09

4

讓我們這兩個數字超過11位左移:

38751876912.5
38751876912.50002636113061835

用銀行家舍,我們輪第一個下來。在中點舍入系統,我們圍繞第二個數字(因爲它不在中點)

.net正在做我們期望的。