十进制不能正确舍入

时间:2016-05-06 09:09:46

标签: c# decimal rounding-error

我有一个名为'sum'的小数,其值为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 个答案:

答案 0 :(得分: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);

答案 1 :(得分: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,这对你来说可能更好。

答案 2 :(得分:0)

这是因为Math.Round方法。

以下是MSDN documentation

  

此方法等效于使用MidpointRounding.ToEven的模式参数调用Round(Decimal,Int32,MidpointRounding)方法。当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

来自备注:

  

decimals参数指定返回值中的有效小数位数,范围为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.8,-3.75和-3.85都是-3.8。这种舍入形式由MidpointRounding.ToEven枚举成员表示。   舍入到最接近的是财务和统计操作中使用的标准舍入形式。它符合IEEE标准754第4节。当用于多个舍入操作时,它减少了由于在单个方向上始终舍入中点值而导致的舍入误差。在某些情况下,这种舍入误差可能很大。

同时检查此.NET fiddle。如果您运行它,您将准确地看到您的预期值5824088.999