The two ways a ratio split invents money or loses it
· 9 min read
Splitting 100 across 1:1:1 gives 33.33 three times, which sums to 99.99. Why the split is a cent short, and how largest-remainder allocation fixes it.

Split a total by a ratio, round each share to two decimals, and the parts often stop adding up to the total they came from. Usually the gap is a hundredth and nobody notices. Twice in a few hundred splits it is not: you are a cent short, or you invented a unit that was never in the money.
Neither failure needs an arithmetic mistake. Both come from computing each share correctly, rounding it alone, and never reconciling against the whole. Rounding one share answers a question about that share; splitting a total is a question about the whole.
Key Takeaways
- Splitting 100.00 across
1:1:1gives 33.33 three times: 99.99. You are a cent short.- Splitting 999.99 across
1:2:3:4gives 100.00 / 200.00 / 300.00 / 400.00, which is 1000.00. You invented money.- Flooring every part, then handing out the leftover whole units, closes the sum by construction.
- A closing sum is necessary but not sufficient, and
Fractioncannot repair a float.
The shortfall: 100.00 split three ways comes to 99.99
Start with the friendliest input: a total of 100.00 across 1:1:1. The weight sum is 3, so each exact share is 100.00 × 1/3 = 33.3333…. That decimal never terminates, so it has no exact two-decimal form. Something must give, and the usual choice is rounding each share on its own:
| Share | Exact | Rounded |
|---|---|---|
| A | 33.3333… | 33.33 |
| B | 33.3333… | 33.33 |
| C | 33.3333… | 33.33 |
| Sum | 100.00 | 99.99 |
A cent has vanished. Note the direction, because it is not the one people expect: the instinctive fear is that rounding hands out too much, so the reader who is surprised is the one short.
The phantom unit: 999.99 split four ways comes to 1000.00
Now the direction that actually invents money. A total of 999.99 across 1:2:3:4, weight sum 10:
| Share | Weight | Exact | Rounded |
|---|---|---|---|
| A | 1 | 99.999 | 100.00 |
| B | 2 | 199.998 | 200.00 |
| C | 3 | 299.997 | 300.00 |
| D | 4 | 399.996 | 400.00 |
| Sum | 999.99 | 1000.00 |
Every share rounds up. Each exact value sits a hair below a round number, so rounding pushes all four over the boundary and the parts sum to a unit more than the total. Invoice those numbers and you have billed 1000.00 for a 999.99 pot.
The ratio is not the culprit. Take the same 1:2:3:4 and split 100.00: you get exactly 10.00 / 20.00 / 30.00 / 40.00, the leftover count is zero, and independent rounding is right. What decides whether a split breaks is whether the total divides evenly by the weight sum at your precision, a property of the total, not the ratio.
Rounding each part is not the same as splitting a total
Rounding each share independently optimises it alone: the nearest two-decimal value to that share's exact amount. It answers "what is this share, rounded?" and says nothing about the sum.
Allocating a total optimises the set instead: shares whose total equals the input, at the cost of pushing at least one away from its own nearest value. In the 100.00 example the honest allocation makes one person 33.34, a hundredth above what independent rounding gave them, so the other two keep their nearest values and the sum closes. That is one cent on an invoice, one unit of stock, one seat on a committee.
The fix: floor everything, then hand out the leftovers
Take the total in units of the smallest place you care about (hundredths here, so 100.00 becomes 10000). Divide to get each share in those units, then floor every share, discarding the fractional part. Count the units flooring threw away, then hand them out one each to the shares with the largest discarded remainders. That count is an integer, which makes the step exact.
Flooring is what makes the method sound. Rounding is where ambiguity enters: each share is nudged up or down and nothing coordinates. Flooring moves one way only, so the running total can only fall short, by a whole number of units.
For 100.00 across 1:1:1: each share floors to 3333, giving 9999 against 10000. One unit is unassigned, and since all three remainders are equal, any share can take it. Give it to A and you have 33.34 / 33.33 / 33.33 = 100.00.
For 999.99 across 1:2:3:4: the shares floor to 9999 / 19999 / 29999 / 39999 = 99996 against 99999. Three units are unassigned, and the remainders rank A 0.9, B 0.8, C 0.7, D 0.6, so A, B and C each take one, giving 100.00 / 200.00 / 300.00 / 399.99 = 999.99.
The first three shares came out identical to naive rounding, and only the last differs. Allocating does not mean pushing every number around; the sum is right and the adjustment lands where the discarded fractions say.
This is the algorithm the ratio calculator runs, which is why it marks the share that absorbed each leftover unit with a + rather than leaving you to infer where the correction went. When a total is too small to divide at your precision, it reports the difference.
One detail there looks wrong and is not. A share's percentage is weight / weightSum, a property of the ratio alone: across 1:2:3:4 the shares are 10%, 20%, 30% and 40% whether the total is 999.99 or 9,999,999. Those are rounded for display so the column never reads 99.99%. The money still reconciles by the rule above.
Where the leftover lands is a policy choice
Once you know the leftover units must land somewhere, the next question is who gets them, and no answer is simply correct. That is what apportionment methods decide when votes become seats.
Wikipedia's largest remainder article describes the quota family: give every party its exact quota, floor it, then hand the leftover seats to "the 'plurality' winners (the parties with the largest remainders, i.e. most leftover votes)". Paired with the Hare quota that rule is Hamilton's method. The same article is blunt: quota methods are "generally disfavored by social choice theorists as a result of apportionment paradoxes", and exhibit "the no-show paradox".
The D'Hondt method, also called Jefferson or greatest divisors, gives each seat to whichever party has the highest votes / (seats so far + 1), and its bias is explicit: it "favors larger political parties over small parties".
The third method is usually called two names and is one method. The Sainte-Laguë article calls it "also called the Webster method or the Schepers method", and says the two "should be treated as two methods with the same result, because the Webster method is used for allocating seats based on states' population, and the Sainte-Laguë based on parties' votes". Same rule, two names — not two rival methods. Its divisor is 2s+1 rather than s+1, and it "shows a more equal seats-to-votes ratio for different sized parties".
Run all three on 27 seats and groups with 1, 10 and 8 votes:
| Method | 1 vote | 10 votes | 8 votes |
|---|---|---|---|
| Hamilton (largest remainder) | 2 | 14 | 11 |
| D'Hondt (Jefferson) | 1 | 15 | 11 |
| Sainte-Laguë (Webster/Schepers) | 1 | 14 | 12 |
All three sum to 27, and all three are used by real governments. The only difference is where the leftover seat went: Hamilton gave it to the smallest group, D'Hondt to the largest, Sainte-Laguë to the middle. That is the tie-break, a choice with no arithmetic answer behind it.
The ratio calculator uses largest remainder and says so in the output. It is a defensible default, since largest remainder keeps every share within one unit of its exact value, but not the only one.
A closing sum is not proof of a correct split
Once a sum closes, the temptation is to treat the split as verified. For 999.99 across 1:2:3:4, two splits both close:
| Split | A | B | C | D | Sum |
|---|---|---|---|---|---|
| Largest remainder | 100.00 | 200.00 | 300.00 | 399.99 | 999.99 |
| Unit moved to the largest | 99.99 | 200.00 | 300.00 | 400.00 | 999.99 |
Both sum to the total, and both are made of numbers rounded to two decimals. One moves the leftover unit from the smallest share to the biggest, a very different outcome for the two people at the ends. Nothing in the sums distinguishes them.
That is structural, not accidental. Closing the sum is one equation and a four-way split has three degrees of freedom, so infinitely many splits satisfy it. A closing sum shows no money was created or destroyed, not that the split was right. Knowing that takes the rule meant to decide it, which is a question about the rule and not the arithmetic.
The second trap: binary floating point
A total can also fail to reconcile for a reason unrelated to ratios: 0.1 + 0.2 == 0.3 evaluates to False in most languages and produces 0.30000000000000004.
The cause is that decimal fractions are not representable in binary. Python's floating-point tutorial explains it: "no matter how many base 2 digits you're willing to use, the decimal value 0.1 cannot be represented exactly as a base 2 fraction". The stored value is the rational 3602879701896397 / 2^55, close to one tenth but not one tenth.
The remedy is to stop using binary floats for anything that must reconcile. Python's decimal module states the case: "Decimal numbers can be represented exactly. In contrast, numbers like 1.1 and 2.2 do not have exact representations in binary floating point", and is "preferred in accounting applications which have strict equality invariants". JavaScript has the same problem: Number is an IEEE 754 double, and past 2^53 consecutive integers are not representable.
The two errors compound. Parts that miss by a hundredth have a rounding bug; parts that miss by 1.4e-14 have a float bug. They look identical in the output and need opposite fixes.
Fraction does not repair a float
The obvious fix is exact rational arithmetic, and the instinct is right, though one sharp edge catches anyone testing only the happy path.
Fraction is exact; that is its whole point. But Fraction(0.1) is an exact representation of the wrong number, carrying every bit of error already baked into the float and then doing arithmetic on it flawlessly.
>>> from fractions import Fraction
>>> Fraction(0.1) == Fraction(1, 10)
False
>>> Fraction(0.1) - Fraction(1, 10)
Fraction(1, 180143985094819840)
>>> Fraction(0.1) + Fraction(0.2) == Fraction(3, 10)
False
Exact arithmetic on a poisoned input produces exact wrong answers, silently. The damage happened before the Fraction was built.
The fix is to keep the decimal out of binary, parsing it from the string you were given:
>>> from decimal import Decimal
>>> Fraction(Decimal("0.1")) + Fraction(Decimal("0.2")) == Fraction(3, 10)
True
The string "0.1" parses as the exact decimal one tenth, so the rational built from it is right and stays right. This is why the ratio calculator carries quantities as integer-scaled decimals end to end rather than converting to a float at the first opportunity.
How to check any allocator yourself
You do not need to trust anyone's implementation or read their code. Add the shares. If they equal the total, no money was created or lost, a check worth running on any number you did not produce yourself. The ratio calculator shows the Σ line by default so it costs nothing; a tool that hides its sum asks you to trust it on a question you can answer in one addition.
If they do not match, you have three possibilities rather than one: shares were rounded independently, the arithmetic passed through a binary float, or the total was too small to divide at your precision. Each needs a different fix.
The best-known tools named "ratio calculator" are proportion solvers, not allocators. OmniCalculator's ratio page says it will "help you compute identical ratios given three of the four parts of the two ratios", and Calculator.net's asks you to "provide any three values below to calculate the fourth in the ratio A:B = C:D". Both take a proportion and find its missing term. Neither takes a total and a ratio and returns parts: a different problem, not a broken version of the same one. How allocators behave is a separate question this post has not surveyed.
When allocation is the wrong tool
Not every quantity needs its parts to reconcile. If the parts are continuous rather than countable, like kilograms of ore or hours of work, rounding is presentational, and forcing closure invents precision the data never had. Allocation matters when the total is fixed inventory: money, seats, stock. The same goes for approximate inputs: an estimated ratio forced to an exact sum overstates what anyone knows. Some accounting setups also require the residual to land on a designated account. Where that rule exists, largest remainder is the wrong tool by construction.
Related tools and further reading
- Ratio calculator — splits a total by a ratio using exact integers and marks which share absorbed each leftover unit with a
+. - Aspect ratio calculator — a geometric width-to-height relationship, usually two terms, with no total to reconcile.
- WCAG contrast ratio guide — also a ratio, also two numbers. If you searched "ratio" expecting something about dividing a total, that is not it.

Written by
Tilly Ashworth
The subject is small and the disagreements are large, so I state the convention on every page rather than assume one. A change from eight to ten is an increase of twenty-five percent, and ten is one hundred and twenty-five percent of eight, and both statements are true and neither is the answer to the other question. Percentage points versus percent is the same trap in another guise, and it appears in reporting often enough to be worth naming separately.
Counting is where dates go wrong. Whether you count both endpoints decides the answer to every age and duration question, and the two conventions give different results for every date range rather than occasionally. Same-day birthdays are one case where the choice is visible.
Leap years are the second. Every four years except centuries not divisible by four hundred, and the rule has an exception that catches people born in or around one. Time zones are the third, and they matter more than they appear to because a date is an instant plus a place, and the place determines the calendar day. Round at the end is the fourth, and it is arithmetic rather than convention, but it is the one calculators get wrong most visibly.
I would rather a calculator state its conventions on the page than be quietly right on one interpretation and quietly wrong on the other. State the convention and the answer is arguable. Hide it and the answer is wrong somewhere without either implementation being at fault.