Десятичное число не округляется должным образом

У меня есть десятичная дробь под названием "сумма" и его значение 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);

Это из-за Math.Round метод.

Вот документация MSDN.

Этот метод эквивалентен вызову метода Round(Decimal, Int32, MidpointRounding) с аргументом режима MidpointRounding.ToEven. Когда d находится точно посередине между двумя округленными значениями, результатом является округленное значение с четной цифрой в крайнем правом углу. десятичная позиция. Например, при округлении до двух десятичных знаков значение 2, 345 становится 2, 34, а значение 2, 355 становится 2, 36. Этот процесс известен как округление к четному или банковское округление. Это сводит к минимуму ошибки округления, возникающие в результате последовательного округления значения средней точки в одном направлении.

Вы должны попробовать Math.Round(decimal, int32, System.MidpointRounding), который можно найти здесь

Попробуйте позвонить с 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,75 и -3,85 округляют до -3,8. Эта форма округления представлена ​​элементом перечисления MidpointRounding.ToEven. Округление до ближайшего является стандартной формой округления, используемой в финансовых и статистических операциях. Он соответствует стандарту IEEE 754, раздел 4. При использовании в нескольких операциях округления он уменьшает ошибку округления, вызванную последовательным округлением значений средней точки в одном направлении. В некоторых случаях эта ошибка округления может быть значительной.

Проверьте также эту .NET скрипку. Если вы запустите его, вы увидите точно ожидаемое значение 5824088.999

Вы должны взглянуть на 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, что может быть лучше для вас.

Другие вопросы по тегам