2016-05-06 115 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的答案) –