Здравствуйте, Ed.Nixon, Вы писали: EN>Подскажите как правильно работать в c# с деньгами.
При работе с деньгами (если это бухгалтерия) важно чтобы выполнялся закон: Сколько пришло столько и должно уйти.
Т.к. при делении элементарных типов (вроде decimal) возможны отклонения от этого закона (т.е. в результате сумма не равна частям) то вывод такой:
Деление нельзя использовать (в случае если не можешь доказать, что оно не создаст проблем).
На аналитическом уровне хорошо бы иметь тип данных "деньги" к которому неприменима операция деления, а приминима лишь операция "распределения" между Н-адресатами по определенному алгоритму. Алгоритм можешь и сам придумать (тока потом согласуй с бухгалтером, чтобы небыло сюрпризов).
Например: если надо поделить 13 копеек между 5 людьми, то каждому должно уйти
13 / 5 = 2 обязательно и возможно + 1 если он из тех счастивчиков между которыми (возможно) равномерно распределяется остаток. Итого ничего не исчезло, бухгалтер доволен.
На уровне реализации сам тип данных (класс) иметь не обязательно, по ситуации.
EN>Стоит задача — приходит сумма денег, ее надо раскидать 70% туда, 5% сюда и т.п. EN>Деньги приходят в usd/eur/rur (ну не суть наверное). EN>Язык C#, использую decimal везде. EN>В конце расчетов сумма прихода и расхода не совпадают. EN>В SQL Server хранится все с точностью (18,2) (может проблема в этом ?) EN>Подскажите как правильно работать в c# с деньгами.
Правильный ответ — "а сколько надо" ? Ну или "позвоните своему бухгалтеру". Потому как в реальной жизни из-за округления сумма у вас скорее всего принципиально сходиться не будет. Потому лучше узнайте как надо не с точки зрения математики и программирования, а с точки зрения предметной области.
Кстати, проверьте еще и округление — не факт, что оно в вашем языке программирования и базе данных именно банковское.
P.S. Когда-то я писал супер-программу для автобазы. В том числе на считала премии за экономию топлива. Алгоритм расчета очень круто считал с очень повышенной точностью, знаков 10 после запятой. Как-то я поинтересовался всей цепочкой — оказалось, что на входе данные от шофера, который смотрит на топливометр с точностью деления +-5 литров. После этого я перестал гоняться за математической правильностью и всегда сразу узнаю у заказчика — чего ему конкретно надо, в каком виде, с какой точностью, какие на то есть постановления и, главное — как это считают руками.
Стоит задача — приходит сумма денег, ее надо раскидать 70% туда, 5% сюда и т.п.
Деньги приходят в usd/eur/rur (ну не суть наверное).
Язык C#, использую decimal везде.
В конце расчетов сумма прихода и расхода не совпадают.
В SQL Server хранится все с точностью (18,2) (может проблема в этом ?)
Подскажите как правильно работать в c# с деньгами.
Здравствуйте, 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. Ну как минимум в спецификации...
Здравствуйте, 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-х знаков после запятой. Последнюю сумму считают вычитанием предыдущих частей из целого. У бухгалтера/менеджера есть возможность вручную корректировать итоговые суммы.