小数不正确四舍五入

Decimal not rounding properly

我有一个名为 '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

列表在另一种方法中。

您应该查看 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,这可能更适合你。

看来你有舍入错误累加 所以总数是错误的:

  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);

是因为Math.Round方法。

这是 MSDN documentation

This method is equivalent to calling the Round(Decimal, Int32, MidpointRounding) method with a mode argument of MidpointRounding.ToEven.When d is exactly halfway between two rounded values, the result is the rounded value that has an even digit in the far right decimal position. For example, when rounded to two decimals, the value 2.345 becomes 2.34 and the value 2.355 becomes 2.36. This process is known as rounding toward even, or banker's rounding. It minimizes rounding errors that result from consistently rounding a midpoint value in a single direction.

你应该试试 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

来自评论:

The decimals parameter specifies the number of significant decimal places in the return value and ranges from 0 to 28. If decimals is zero, an integer is returned.

In a midpoint value, the value after the least significant digit in the result is precisely half way between two numbers. For example, 3.47500 is a midpoint value if it is to be rounded two decimal places, and 7.500 is a midpoint value if it is to be rounded to an integer. In these cases, the nearest value can't be easily identified without a rounding convention, which is specified by the mode argument. The Round(Decimal, Int32, MidpointRounding) method supports two rounding conventions for handling midpoint values.

Rounding away from zero. Midpoint values are rounded to the next number away from zero. For example, 3.75 rounds to 3.8, 3.85 rounds to 3.9, -3.75 rounds to -3.8, and -3.85 rounds to -3.9. This form of rounding is represented by the MidpointRounding.AwayFromZero enumeration member. Rounding away from zero is the most widely known form of rounding.

Rounding to even, or banker's rounding Midpoint values are rounded to the nearest even number. For example, both 3.75 and 3.85 round to 3.8, and both -3.75 and -3.85 round to -3.8. This form of rounding is represented by the MidpointRounding.ToEven enumeration member. Rounding to nearest is the standard form of rounding used in financial and statistical operations. It conforms to IEEE Standard 754, section 4. When used in multiple rounding operations, it reduces the rounding error that is caused by consistently rounding midpoint values in a single direction. In some cases, this rounding error can be significant.

也检查此 .NET fiddle。如果你 运行 它,你会准确地看到你的期望值 5824088.999