Rounding issues with the distribution of dollar amounts between multiple people

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

The problem is that I have an amount of $ 2 (known as a bank) that I need to allocate to 3 people. Each person receives a certain amount, which comes from both pots, and the rates should be approximately the same. I continue to encounter rounding issues when my allocation is either too complicated or too small.

Here is an example:

Pot No. 1 987 654.32
Pot No. 2 123 456.78

Person No. 1 receives the appropriation amount: 345,678.89
Person No. 2 receives the appropriation amount: 460 599.73
Person No. 3 receives the appropriation 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 No. 1, Person No. 1 = 307,270.13
Pot No. 1, Person No. 2 = 409,421.99
Pot No. 1, Person No. 3 = 270 962.21
Pot No. 1 Total = 987 654.33 (1 penny)

Pot No. 2, Man No. 1 = 38,408.76
Pot No. 2, Person No. 2 = 51,177.74
Pot No. 2, Person No. 3 = 33,870.27
Pot No. 2 Total = 123,456.77 (1 kopeck)

The results of the sweat should correspond to the initial totals.

I think I can miss something, or there may be an extra step that I need to take. I think I'm on the right track.

Any help would be greatly appreciated.

+7
c # rounding puzzle
source share
6 answers

This happens in financial calculations when rounding to the nearest penny. No amount of rounding algorithm settings for individual operations will work for each case.

You must have a battery that keeps track of the amount allotted after the rounding and distribution operation. At the end of the distribution, you check the battery for actual results (summarized together) and distribute the remaining pennies.

In the math example below, if you take 0.133 and round it to 0.13 and add 3 times, you will get a penny less than if you first added 0.133 3 times and then round.

  0.13 0.133 0.13 0.133 +0.13 +0.133 _____ ______ 0.39 0.399 -> 0.40 
+12
source share

Have you tried to associate rounding behavior with the MidpointRounding argument?

 public static decimal Round( decimal d, MidpointRounding mode ) 
+2
source share

+1 for Matt Spradley's solution.

As an additional comment on Matt's decision, you should also consider the case when you ultimately give out a penny (or more) less than the target amount - in this case you need to deduct the money from one or more of the allocated amounts.

You also need to make sure that you do not deduct a penny from the amount allocated in the amount of 0.00 (in case you allocate a very small amount among a large number of recipients).

+2
source share

Definitely Math.Round.

I would suggest not rounding the result of the calculation, but if you need to display, then round to the nearest penny. Or you can use a penny as the smallest denominator, so when displaying, we divide everything by 100.

+1
source share

I think this is exactly the problem Eric Evans addresses in his Driven Design . Chapter 8, pp. 198-203.

+1
source share

What to do when sharing money is a long-standing problem. Martin Fowler offers some comments here (I think in his actual PoEAA ):

But the separation is not [straightforward], because we must take care of the erroneous pennies. We will do this by returning an array of funds, so that the amount of the array will be equal to the original amount, and the original amount will be distributed fairly between the elements of the array. Fair in this sense means that in the beginning they get 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; } 
+1
source share

All Articles