Стоит задача — приходит сумма денег, ее надо раскидать 70% туда, 5% сюда и т.п.
Деньги приходят в usd/eur/rur (ну не суть наверное).
Язык C#, использую decimal везде.
В конце расчетов сумма прихода и расхода не совпадают.
В SQL Server хранится все с точностью (18,2) (может проблема в этом ?)
Подскажите как правильно работать в c# с деньгами.
EN>Стоит задача — приходит сумма денег, ее надо раскидать 70% туда, 5% сюда и т.п. EN>Деньги приходят в usd/eur/rur (ну не суть наверное). EN>Язык C#, использую decimal везде. EN>В конце расчетов сумма прихода и расхода не совпадают. EN>В SQL Server хранится все с точностью (18,2) (может проблема в этом ?) EN>Подскажите как правильно работать в c# с деньгами.
Правильный ответ — "а сколько надо" ? Ну или "позвоните своему бухгалтеру". Потому как в реальной жизни из-за округления сумма у вас скорее всего принципиально сходиться не будет. Потому лучше узнайте как надо не с точки зрения математики и программирования, а с точки зрения предметной области.
Кстати, проверьте еще и округление — не факт, что оно в вашем языке программирования и базе данных именно банковское.
P.S. Когда-то я писал супер-программу для автобазы. В том числе на считала премии за экономию топлива. Алгоритм расчета очень круто считал с очень повышенной точностью, знаков 10 после запятой. Как-то я поинтересовался всей цепочкой — оказалось, что на входе данные от шофера, который смотрит на топливометр с точностью деления +-5 литров. После этого я перестал гоняться за математической правильностью и всегда сразу узнаю у заказчика — чего ему конкретно надо, в каком виде, с какой точностью, какие на то есть постановления и, главное — как это считают руками.
Здравствуйте, Ed.Nixon, Вы писали:
EN>В конце расчетов сумма прихода и расхода не совпадают.
Decimal не избавляет от проблем с округлением:
var total = 1000M;
var partsCount = 13;
var parts = new decimal[partsCount];
for (var i = 0; i < partsCount; i++)
{
parts[i] = total / partsCount;
};
Console.WriteLine(parts.Sum()); // 999,9999999999999999999999999
EN>В SQL Server хранится все с точностью (18,2) (может проблема в этом ?)
Увеличить точность можно, сложнее объяснить бухгалтерии, как перечислить 100,34455566778899 копеек.
EN>Подскажите как правильно работать в c# с деньгами.
Зависит от ситуации. В вашем случае я бы поступил так:
var total = 1000M;
var partsCount = 13;
var parts = new decimal[partsCount];
for (var i = 0; i < partsCount - 1; i++)
{
parts[i] = total / partsCount;
};
parts[partsCount - 1] = total - parts.Take(partsCount - 1).Sum();
Console.WriteLine(parts.Sum()); // 1000,0000000000000000000000000
Здравствуйте, Nikolay_P_I, Вы писали:
N_P>Кстати, проверьте еще и округление — не факт, что оно в вашем языке программирования и базе данных именно банковское.
У C#'ного decimal как раз banker's rounding. Ну как минимум в спецификации...
Здравствуйте, Ed.Nixon, Вы писали: EN>Подскажите как правильно работать в c# с деньгами.
При работе с деньгами (если это бухгалтерия) важно чтобы выполнялся закон: Сколько пришло столько и должно уйти.
Т.к. при делении элементарных типов (вроде decimal) возможны отклонения от этого закона (т.е. в результате сумма не равна частям) то вывод такой:
Деление нельзя использовать (в случае если не можешь доказать, что оно не создаст проблем).
На аналитическом уровне хорошо бы иметь тип данных "деньги" к которому неприменима операция деления, а приминима лишь операция "распределения" между Н-адресатами по определенному алгоритму. Алгоритм можешь и сам придумать (тока потом согласуй с бухгалтером, чтобы небыло сюрпризов).
Например: если надо поделить 13 копеек между 5 людьми, то каждому должно уйти
13 / 5 = 2 обязательно и возможно + 1 если он из тех счастивчиков между которыми (возможно) равномерно распределяется остаток. Итого ничего не исчезло, бухгалтер доволен.
На уровне реализации сам тип данных (класс) иметь не обязательно, по ситуации.
Здравствуйте, HowardLovekraft, Вы писали:
HL>Зависит от ситуации. В вашем случае я бы поступил так: HL>
HL> var total = 1000M;
HL> var partsCount = 13;
HL> var parts = new decimal[partsCount];
HL> for (var i = 0; i < partsCount - 1; i++)
HL> {
HL> parts[i] = total / partsCount;
HL> };
HL> parts[partsCount - 1] = total - parts.Take(partsCount - 1).Sum();
HL> Console.WriteLine(parts.Sum()); // 1000,0000000000000000000000000
HL>
Предложил бы дополнить: менять при каждой следующей операции куда прибавляется остаток.
Можно реализовать денежную операцию деления:
напр: 10 / 3 = 3,33 (сколько знаков оговорено бухгалтером)
Здравствуйте, Ed.Nixon, Вы писали:
EN>Здраствуйте,
EN>Стоит задача — приходит сумма денег, ее надо раскидать 70% туда, 5% сюда и т.п. EN>Деньги приходят в usd/eur/rur (ну не суть наверное). EN>Язык C#, использую decimal везде. EN>В конце расчетов сумма прихода и расхода не совпадают.
EN>В SQL Server хранится все с точностью (18,2) (может проблема в этом ?)
EN>Подскажите как правильно работать в c# с деньгами.
EN>Спасибо!
На практике встречал такое. Округляют до 4-х знаков после запятой. Последнюю сумму считают вычитанием предыдущих частей из целого. У бухгалтера/менеджера есть возможность вручную корректировать итоговые суммы.