8 Stimmen

Rundungsprobleme bei der Aufteilung von Dollarbeträgen auf mehrere Personen

Wie lässt sich dieses Problem am besten im Code lösen?

Das Problem ist, dass ich 2 Dollarbeträge (bekannt als Topf) habe, die auf 3 Personen aufgeteilt werden müssen. Jede Person erhält einen bestimmten Betrag, der aus beiden Töpfen stammt, und die Sätze müssen ungefähr gleich sein. Ich stoße immer wieder auf Rundungsprobleme, bei denen sich meine Zuweisungen entweder zu viel oder zu wenig summieren.

Hier ist ein konkretes Beispiel:

Topf Nr. 1 987.654,32
Topf #2 123.456,78

Person 1 erhält den Zuteilungsbetrag: 345,678.89
Person Nr. 2 erhält den Zuteilungsbetrag: 460,599.73
Person #3 erhält Zuteilungsbetrag: 304,832.48

Meine Logik ist wie folgt (Code ist 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;
        }
    }
}

Die Ergebnisse sind wie folgt:

Topf 1, Person 1 = 307.270,13
Topf #1, Person #2 = 409.421,99
Topf #1, Person #3 = 270.962,21
Topf 1 insgesamt = 987.654,33 (1 Penny weniger)

Topf #2, Person #1 = 38.408,76
Topf #2, Person #2 = 51.177,74
Topf #2, Person #3 = 33.870,27
Topf #2 insgesamt = 123.456,77 (1 Penny weniger)

Die Summen der Töpfe sollten mit den ursprünglichen Summen übereinstimmen.

Ich glaube, ich übersehe etwas, oder es gibt vielleicht einen zusätzlichen Schritt, den ich tun muss. Ich denke, ich bin auf dem richtigen Weg.

Für jede Hilfe wären wir Ihnen sehr dankbar.

14voto

Matt Spradley Punkte 7114

Dies kommt bei finanziellen Berechnungen häufig vor, wenn auf den nächsten Penny gerundet wird. Es gibt keine Möglichkeit, den Rundungsalgorithmus für die einzelnen Operationen so zu verändern, dass er in jedem Fall funktioniert.

Sie müssen über einen Akkumulator verfügen, der den nach der Rundung und Verteilung zugewiesenen Betrag verfolgt. Am Ende der Zuteilungen vergleichen Sie den Akkumulator mit den tatsächlichen Ergebnissen (die zusammengerechnet werden) und verteilen den übrig gebliebenen Penny.

Im folgenden Rechenbeispiel erhält man einen Cent weniger, wenn man 0,133 nimmt, auf 0,13 rundet und dreimal addiert, als wenn man 0,133 zuerst dreimal addiert und dann rundet.

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

3voto

Jon Schneider Punkte 23345

+1 für die Lösung von Matt Spradley.

Als zusätzliche Anmerkung zu Matts Lösung müssen Sie natürlich auch den Fall berücksichtigen, dass Sie am Ende einen Penny (oder mehr) zuweisen weniger als der Zielbetrag - in diesem Fall müssen Sie Geld von einem oder mehreren der zugewiesenen Beträge abziehen.

Sie müssen auch darauf achten, dass Sie von einem zugewiesenen Betrag von 0,00 $ keinen Cent abziehen (falls Sie einen sehr kleinen Betrag auf eine große Anzahl von Empfängern verteilen).

2voto

Brian Reiter Punkte 1329

Haben Sie versucht, die Rundung Verhalten mit dem MidpointRounding Argument conntrolling?

public static decimal Round( decimal d, MidpointRounding mode )

1voto

AakashM Punkte 60642

Die Aufteilung von Geld ist ein immerwährendes Problem. Martin Fowler bietet einige Kommentare aquí (Ich glaube, es gibt mehr Details in seinem aktuellen PoEAA Buch):

Aber die Teilung ist nicht [einfach], denn wir müssen uns um die verirrten Pfennige kümmern. Wir werden das tun, indem wir ein Array von Geldbeträgen zurückgeben, so dass die Summe des Arrays gleich dem ursprünglichen Betrag ist und der ursprüngliche Betrag gerecht auf die Elemente des Arrays verteilt wird. Fair in diesem Sinne bedeutet, dass die am Anfang stehenden Elemente die zusätzlichen Pfennige erhalten.

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

0voto

Adrian Godong Punkte 8573

Definitiv die Math.Round.

Ich würde vorschlagen, das Rechenergebnis nicht zu runden, aber wenn Sie es anzeigen müssen, dann runden Sie auf den nächsten Cent. Oder Sie können Pfennige als kleinsten Nenner verwenden und bei der Anzeige alles durch 100 dividieren.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X