Десятичное число не округляется должным образом
У меня есть десятичная дробь под названием "сумма" и его значение 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
, что может быть лучше для вас.