向多人分配美元金额的四舍五入问题

发布于 2024-07-22 13:06:33 字数 1640 浏览 4 评论 0 原文

在代码中解决这个问题的最佳方法是什么?

问题是我有 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 便士)

底池总金额应与原始总金额相符。

我想我可能遗漏了一些东西,或者可能需要采取额外的步骤。 我认为我走在正确的轨道上。

任何帮助将不胜感激。

What is the best way to solve this problem in code?

The problem is that I have 2 dollar amounts (known as a pot), that need to be allocated to 3 people. Each person gets a specific amount that comes from both pots and the rates must be approximately the same. I keep coming across rounding issues where my allocations either add up to too much or too little.

Here is a specific example:

Pot #1 987,654.32
Pot #2 123,456.78

Person #1 gets Allocation Amount: 345,678.89
Person #2 gets Allocation Amount: 460,599.73
Person #3 gets Allocation Amount: 304,832.48

My logic is as follows (Code is in 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;
        }
    }
}

The results I get are as follows:

Pot #1, Person #1 = 307,270.13
Pot #1, Person #2 = 409,421.99
Pot #1, Person #3 = 270,962.21
Pot #1 Total = 987,654.33 (1 penny off)

Pot #2, Person #1 = 38,408.76
Pot #2, Person #2 = 51,177.74
Pot #2, Person #3 = 33,870.27
Pot #2 Total = 123,456.77 (1 penny off)

The Pot Totals should match the original totals.

I think I may be missing something or there may be an extra step that I need to take. I think I am on the right track.

Any help would be greatly appreciated.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

治碍 2024-07-29 13:06:33

当四舍五入到最接近的美分时,这种情况在财务计算中经常发生。 对各个操作舍入算法进行再多的调整都不适用于每种情况。

您必须有一个累加器来跟踪舍入和分配操作后分配的金额。 在分配结束时,您根据实际结果(总和)检查累加器并分配剩余的便士。

在下面的数学示例中,如果您将 0.133 四舍五入为 0.13,然后再加 3 次,那么您得到的结果会比先将 0.133 加 3 次然后四舍五入少一分钱。

 0.13    0.133
 0.13    0.133
+0.13   +0.133
_____   ______
 0.39    0.399 -> 0.40

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.

 0.13    0.133
 0.13    0.133
+0.13   +0.133
_____   ______
 0.39    0.399 -> 0.40
烟─花易冷 2024-07-29 13:06:33

马特·斯普拉德利的解决方案+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).

剑心龙吟 2024-07-29 13:06:33

您是否尝试过使用 MidpointRounding 参数来控制舍入行为?

public static decimal Round( decimal d, MidpointRounding mode )

Have you tried conntrolling the rounding behavior with the MidpointRounding argument?

public static decimal Round( decimal d, MidpointRounding mode )
佞臣 2024-07-29 13:06:33

分配钱时该怎么办是一个长期存在的问题。 Martin Fowler 在此处提供了一些评论(我认为他的实际PoEAA 书):

但是除法并不[直接],因为我们必须处理错误的便士。 我们将通过返回一个货币数组来实现这一点,使得数组的总和等于原始金额,并且原始金额在数组的元素之间公平分配。 从这个意义上讲,公平意味着那些一开始就得到额外的钱。

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;
    }

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):

But division is not [straightforward], as we have to take care of errant pennies. We'll do that by returning an array of monies, such that the sum of the array is equal to the original amount, and the original amount is distributed fairly between the elements of the array. Fairly in this sense means those at the beginning get the extra pennies.

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;
    }
千秋岁 2024-07-29 13:06:33

绝对是数学回合。

我建议不要对计算结果进行四舍五入,但如果需要显示,则四舍五入到最接近的便士。 或者您可以使用便士作为最小分母,因此在显示时将所有内容除以 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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文