在代码中解决此问题的最佳方法是什么?
问题是我有2美元的金额(称为底池),需要分配给3个人。每个人获得来自两个盆的特定数量,并且费率必须大致相同。我不断遇到问题,我的分配加起来太多或太少。
这是一个具体的例子:
Pot#1 987,654.32
锅#2 123,456.78
第1号人获得分配金额:345,678.89
人员#2获得分配金额:460,599.73
第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便士)
Pot#2,Person#1 = 38,408.76
锅#2,人#2 = 51,177.74
锅#2,人#3 = 33,870.27
锅#2总计= 123,456.77(1便士)
Pot Totals应与原始总数匹配。
我想我可能会遗漏某些东西,或者我可能需要采取额外措施。我想我走在正确的轨道上。
非常感谢任何帮助。
答案 0 :(得分:13)
当四舍五入到最近的便士时,这在财务计算中会发生很多。对于每种情况,不需要调整单个操作舍入算法的数量。
您必须有一个累加器,用于跟踪舍入和分配操作后分配的金额。在分配结束时,根据实际结果检查累加器(总结在一起)并分配剩余的便士。
在下面的数学示例中,如果你取0.133并将其四舍五入为0.13并且加3次,你会比先加上0.133次然后再加一次0.133便宜一分钱。
0.13 0.133
0.13 0.133
+0.13 +0.133
_____ ______
0.39 0.399 -> 0.40
答案 1 :(得分:2)
您是否尝试使用MidpointRounding参数控制舍入行为?
public static decimal Round( decimal d, MidpointRounding mode )
答案 2 :(得分:2)
+1 Matt Spradley的解决方案。
作为对Matt解决方案的补充评论,您当然还需要考虑最终分配一分钱(或更多)少而非目标金额的情况 - 在这种情况下,您需要从一个或多个分配金额中扣除资金。
您还需要确保不会从分配的0.00美元金额中扣除一分钱(如果您在大量收件人中分配的金额非常小)。
答案 3 :(得分:1)
分钱时该怎么做是一个长期存在的问题。 Martin Fowler提供了一些评论here(我认为他的实际PoEAA书中有更详细的内容):
但是分工并不[直截了当],因为我们必须照顾错误的便士。我们将通过返回一个monies数组来做到这一点,这样数组的总和等于原始数量,并且原始数量在数组的元素之间公平分配。从这个意义上来说,相当意味着那些开始时会获得额外的便士。
class Money...
public Money[] divide(int denominator) {
BigInteger bigDenominator = BigInteger.valueOf(denominator);
Money[] result = new Money[denominator];
BigInteger simpleResult = amount.divide(bigDenominator);
for (int i = 0; i < denominator ; i++) {
result[i] = new Money(simpleResult, currency, true);
}
int remainder = amount.subtract(simpleResult.multiply(bigDenominator)).intValue();
for (int i=0; i < remainder; i++) {
result[i] = result[i].add(new Money(BigInteger.valueOf(1), currency, true));
}
return result;
}
答案 4 :(得分:0)
绝对是Math.Round。
我建议不要舍入计算结果,但如果需要显示,则舍入到最近的便士。或者你可以使用便士作为最小分母,因此在显示时,将所有内容除以100。