2016-05-06 81 views
2

我有一個小數叫'總和',它的值是5824088.999120m,但是當我嘗試將它舍入到3位小數時,我得到5824088.998m而不是5824088.999m。它遞減而不是離開5824088.999m十進制不正確舍入

爲什麼?這裏是我的代碼:

List<decimal> quantityList = QuantityList(); 
List<decimal> priceList = PriceList(); 

decimal destination = 5824088.999M; 
decimal sum = 0M; 
bool lifesaver = false; 

for (int x = 0; x < priceList.Count; x++) 
{ 
    sum = 0M; 
    for (int i = 0; i < 3500; i++) 
    { 
     priceList[x] += 0.001M; 
     sum = 0M; 
     for (int y = 0; y < priceList.Count; y++) 
     { 
      decimal multipleProduct = priceList[y] * quantityList[y]; 
      sum = sum + multipleProduct; 
      Console.WriteLine(priceList[y] + " " + quantityList[y]); 

      sum = Math.Round(sum, 3); 
      Console.WriteLine("Sum: " + sum); 
      Console.ReadKey(); 
      Console.WriteLine(); 
     } 

     if (sum == destination) 
     { 
      Console.WriteLine("The new number is " + priceList[x] + " and it is the {0} element!", x); 
      lifesaver = true; 
      break; 
     } 
     else if (sum > destination) 
     { 
      Console.WriteLine("Limit exceeded!"); 
     } 

     if (i == 3499) 
     { 
      priceList[x] -= 3.500M; 
     } 
     if (lifesaver == true) 
     { 
      break; 
     } 
    }//Second for loop 

    if (lifesaver == true) 
    { 
     break; 
    } 
}//Main for loop 

該列表是在另一種方法。

回答

3

看來你有圍捕錯誤積累,因此總的是錯誤的:

for (int y = 0; y < priceList.Count; y++) { 
    ... 
    sum = Math.Round(sum, 3); // <- this accumulates round up errors 
    ... 
    } 

想象priceList包含

priceList = new List<Decimal>() { 
    1.0004M, 1.0004M, 1.0004M}; 

quantityList全部爲1;該sum

1.000M, 2.000M, 3.000M 

而實際總爲

Math.Round(1.0004M + 1.0004M + 1.0004M, 3) 

3.001M。 可能的解決方案是不是圍捕過早

for (int y = 0; y < priceList.Count; y++) { 
     ... 
     //DONE: comment out this: no premature rounding (within the loop) 
     // sum = Math.Round(sum, 3); 
     //DONE: but format out when printing out 
     Console.WriteLine("Sum: {0:F3}", sum); 
     ... 
    } 

    // round up (if you want) after the loop 
    sum = Math.Round(sum, 3); 
+0

@Reza Taibur:*刪除/註釋掉*'sum = Math.Round(sum,3);' - 只是在循環內不加整數*(過早地),僅添加值並在打印時格式化它們。如果你想收起來,在*循環後執行* –

+0

謝謝!沒有在循環工作中四捨五入! –

0

你應該看看MidpointRounding(https://msdn.microsoft.com/en-us/library/system.midpointrounding(v=vs.110).aspx)並將其添加到Math.Round函數中。像這樣:sum = Math.Round(sum, 3, MidpointRounding.ToEven);,你擁有的其他選項是MidpointRounding.AwayFromZero,這對你可能會更好。

+1

嗯,我確定使用AwayFromZero以不同的方式圍繞ToEven。如果你已經嘗試過了,爲什麼不告訴我們你做過,結果如何。你不能期望我們都知道你所嘗試過的以及你沒有做過什麼。 「不行,我已經嘗試過了。」,對於一個答案來說,這不是一個真正的反應,我會爭辯說。 –

0

這是因爲Math.Round方法。

以下是MSDN documentation

此方法等效於調用回合(十進制,的Int32,MidpointRounding)方法MidpointRounding.ToEven.When d的模式參數是兩個圓形值之間正中間,其結果是具有舍入值甚至數字在最右邊的小數位。例如,四捨五入爲兩位小數時,值2.345變爲2.34,值2.355變爲2.36。這個過程被稱爲四捨五入或銀行家四捨五入。它最大限度地減少了在一個方向上始終舍入中點值所導致的舍入誤差。

你應該嘗試Math.Round(decimal, int32, System.MidpointRounding),可以發現here

嘗試使用MidPointRounding枚舉AwayFromZero值調用它。

實施例:

decimal d = 5824088.999120M; 
decimal rounded = Math.Round(d, 3, System.MidpointRounding.AwayFromZero); 
Console.WriteLine(rounded.ToString()); 

日誌5824088.999

從備註:

的小數參數指定的在返回值顯著小數位的數量和範圍從0到28。如果小數點爲零,則返回一個整數。

在中點值中,結果中最低有效位之後的值恰好在兩個數之間的一半處。例如,如果四捨五入取兩個小數位,則3.47500是一箇中點值,如果四捨五入爲整數,則7.500是一箇中點值。在這些情況下,如果沒有由mode參數指定的四捨五入約定,則無法輕易識別最接近的值。 Round(Decimal,Int32,MidpointRounding)方法支持處理中點值的兩個舍入約定。

從零倒圓。 中點值四捨五入到遠離零的下一個數字。例如,3.75至3.8,3.85至3.9,-3.75至-3.8,-3.85至-3.9。這種舍入形式由MidpointRounding.AwayFromZero枚舉成員表示。 舍入零是最廣爲人知的四捨五入形式。

舍入爲偶數或銀行家舍入 中點值被舍入爲最接近的偶數。例如,3.75和3.85均爲3.8,而-3.75和-3.85均爲-3.8。這種舍入形式由MidpointRounding.ToEven枚舉成員表示。 舍入到最近是財務和統計操作中使用的舍入的標準形式。它符合IEEE標準754第4節。當用於多個舍入操作時,它減少了由於在一個方向上不斷舍入中點值而導致的舍入誤差。在某些情況下,這個舍入誤差可能很重要。

也檢查此.NET fiddle也。如果你運行它,你會看到精確的預期值。5824088.999

+0

我需要的總和不是.000的確切值 –

+0

@RezaTaibur恐怕我不明白你需要什麼。你能否澄清一下你在問題中究竟需要什麼,以便我們能夠幫助你? – gdyrrahitis

+0

謝謝,但我已經得到了答案(請參閱Dmytri Bychenko的答案) –