向多人分配美元金额的四舍五入问题
在代码中解决这个问题的最佳方法是什么?
问题是我有 2 美元金额(称为底池),需要分配给 3 个人。 每个人都会从两个罐子中获得特定的金额,并且比率必须大致相同。 我不断遇到舍入问题,即我的分配加起来太多或太少。
这是一个具体的例子:
Pot #1 987,654.32
Pot #2 123,456.78
Person #1 获得分配金额:345,678.89
#2 人获得分配金额:460,599.73
Person #3 获得分配金额:304,832.48
我的逻辑如下(代码为 c#):
foreach (Person person in People)
{
decimal percentage = person.AllocationAmount / totalOfAllPots;
decimal personAmountRunningTotal = person.AllocationAmount;
foreach (Pot pot in pots)
{
decimal potAllocationAmount = Math.Round(percentage * pot.Amount, 2);
personAmountRunningTotal -= potAllocationAmount;
PersonPotAssignment ppa = new PersonPotAssignment();
ppa.Amount = potAllocationAmount;
person.PendingPotAssignments.Add(ppa);
}
foreach (PersonPotAssignment ppa in person.PendingPotAssignments)
{
if (personAmountRunningTotal > 0) //Under Allocated
{
ppa.Amount += .01M;
personAmountRunningTotal += .01M;
}
else if (personAmountRunningTotal < 0) //Over Allocated
{
ppa.Amount -= .01M;
personAmountRunningTotal -= .01M;
}
}
}
我得到的结果如下:
Pot #1, Person #1 = 307,270.13
底池#1,人物#2 = 409,421.99
底池#1,人#3 = 270,962.21
底池 #1 总计 = 987,654.33(优惠 1 便士)
底池 #2,个人 #1 = 38,408.76
底池#2,人#2 = 51,177.74
底池#2,人#3 = 33,870.27
底池 #2 总金额 = 123,456.77(减 1 便士)
底池总金额应与原始总金额相符。
我想我可能遗漏了一些东西,或者可能需要采取额外的步骤。 我认为我走在正确的轨道上。
任何帮助将不胜感激。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
当四舍五入到最接近的美分时,这种情况在财务计算中经常发生。 对各个操作舍入算法进行再多的调整都不适用于每种情况。
您必须有一个累加器来跟踪舍入和分配操作后分配的金额。 在分配结束时,您根据实际结果(总和)检查累加器并分配剩余的便士。
在下面的数学示例中,如果您将 0.133 四舍五入为 0.13,然后再加 3 次,那么您得到的结果会比先将 0.133 加 3 次然后四舍五入少一分钱。
This happens in financial calculations a lot when rounding to the nearest penny. No amount of tweaking the individual operations rounding algorithm will work for every case.
You have to have an accumulator that tracks the amount allocated after the rounding and distribution operation. At the end of the allocations, you check the accumulator against the actual results (summed together) and distribute the leftover penny.
In the math example below, if you take 0.133 and round it to 0.13 and add 3 times you get a penny less than if you add 0.133 3 times first and then round.
马特·斯普拉德利的解决方案+1。
作为对马特解决方案的附加评论,您当然还需要考虑最终分配的便士(或更多)少于目标金额的情况 - 在这种情况下,您需要减去来自一项或多项分配金额的资金。
您还需要确保最终不会从 0.00 美元的分配金额中减去一分钱(如果您在大量收件人中分配了非常小的金额)。
+1 for Matt Spradley's solution.
As an additional comment to Matt's solution, you of course also need to account for the case where you end up allocating penny (or more) less than the target amount -- in that case, you need to subtract money from one or more of the allocated amounts.
You also need to ensure that you don't end up subtracting a penny from an allocated amount of $0.00 (in the event that you are allocating a very small amount among a large number of recipients).
您是否尝试过使用 MidpointRounding 参数来控制舍入行为?
Have you tried conntrolling the rounding behavior with the MidpointRounding argument?
分配钱时该怎么办是一个长期存在的问题。 Martin Fowler 在此处提供了一些评论(我认为他的实际PoEAA 书):
What to do when dividing money is a perennial problem. Martin Fowler offers some commentary here (I think there is more detail in his actual PoEAA book):
绝对是数学回合。
我建议不要对计算结果进行四舍五入,但如果需要显示,则四舍五入到最接近的便士。 或者您可以使用便士作为最小分母,因此在显示时将所有内容除以 100。
Definitely the Math.Round.
I would suggest not rounding the calculation result, but if you need to display, then round to nearest penny. Or you can use pennies as smallest denominator, thus when displaying, divide everything by 100.