Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rounding issues with allocating dollar amounts across multiple people

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.

like image 628
Jon Avatar asked May 29 '09 01:05

Jon


3 Answers

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
like image 75
Matt Spradley Avatar answered Oct 22 '22 02:10

Matt Spradley


+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).

like image 27
Jon Schneider Avatar answered Oct 22 '22 01:10

Jon Schneider


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

public static decimal Round( decimal d, MidpointRounding mode )
like image 21
Brian Reiter Avatar answered Oct 22 '22 02:10

Brian Reiter