Case math without a calculator
Quantitative work is 30–50% of case time and the place a single arithmetic slip does the most damage. This lesson is the nine formulas that cover almost every case, the shortcuts that make them doable with a pen, how much accuracy each shortcut actually costs, and the four-step way to say a calculation out loud so the interviewer can catch your mistake before it compounds.
What you'll be able to do
- Recall the nine formulas that cover the arithmetic in most cases, including the one whose denominator is usually wrong
- Apply rounding, decomposition, halve-and-double, zero management and the rule of 72, and state the error each introduces
- Verbalise a calculation in four steps — approach, work, sanity check, interpretation
- Name the five mistakes that cause most quantitative failures, and the habit that catches each one
Before this: issue-trees-and-mece
Quantitative work occupies somewhere between a third and a half of a case, and roughly six in ten first-round interviews include an estimation question. It is also, by a wide margin, the phase with the harshest failure mode: coaches who have run these rounds describe a single mental-math error as close to an automatic rejection, because the interviewer cannot tell a slip from a misunderstanding and does not have time to find out.
That sounds like an argument for being fast. It is the opposite. The candidates who pass this phase are rarely the quickest — they are the ones who round deliberately, say what they are about to do before they do it, and check the answer against reality before presenting it.
Start at 3:36 if you already accept that the arithmetic is basic — the section on *why* math is tested at all is the frame that makes the rest of this lesson make sense. The four chapters from 8:48 onward are the same techniques worked below, demonstrated at speaking speed.
Jump to the part you need
The nine formulas
You do not need a library. You need these, and you need to be able to say them without hesitating.
| Quantity | Formula |
|---|---|
| Revenue | Volume × Price |
| Total cost | Fixed cost + Variable cost |
| Profit | Revenue − Total cost |
| Profit margin | Profit ÷ Revenue |
| Breakeven volume | Fixed cost ÷ (Price − Variable cost per unit) |
| Payback period | Initial investment ÷ Annual profit |
| ROI | Annual profit ÷ Initial investment |
| CAGR | (End ÷ Start) to the power of 1/n, minus 1 |
| Weighted average | Σ(value × weight) ÷ Σ(weights) |
One of those nine is a reliable trap. Breakeven volume divides by contribution margin per unit — price minus variable cost per unit — not by total cost per unit. Getting that denominator wrong does not produce a slightly-off number; it produces a number that points at the opposite recommendation. The second cell below shows exactly how far off.
What the shortcuts cost
Every mental-math technique is a trade: less accuracy for more speed. The trade is only defensible if you know its size, so the first cell computes it rather than asserting it.
// Case math is estimation with an error bar you can state out loud, not exact
// calculation. Every shortcut below trades accuracy for speed -- so the useful
// thing to print is how much accuracy the shortcut actually cost.
const cases = [
["487 stores x $1.2M", 487 * 1.2e6, 500 * 1.2e6, "round 487 up to 500"],
["17% of $480M", 0.17 * 480e6, (0.1 + 0.05 + 0.02) * 480e6, "10% + 5% + 2%"],
["7 x $496", 7 * 496, 7 * 500 - 7 * 4, "distribute over (500 - 4)"],
["160 x 350", 160 * 350, 80 * 700, "halve one, double the other"],
["$500M / 150,000", 500e6 / 150000, 500000 / 150, "cancel three zeros each side"],
];
console.log("shortcut exact shortcut error");
for (const [label, exact, quick, how] of cases) {
const err = ((quick - exact) / exact) * 100;
console.log(
" " + label.padEnd(20),
exact.toFixed(0).padStart(11),
quick.toFixed(0).padStart(12),
(err.toFixed(1) + "%").padStart(9),
" " + how
);
}
// Rule of 72: something growing at r% a year doubles in roughly 72/r years.
console.log("");
console.log("rule of 72, against the exact doubling time");
for (const r of [4, 6, 8, 9, 12, 18]) {
const approx = 72 / r;
const exact = Math.log(2) / Math.log(1 + r / 100);
console.log(
" " + (r + "%/yr").padEnd(7),
"72/r =", approx.toFixed(1).padStart(4), "yr ",
" exact =", exact.toFixed(1).padStart(4), "yr ",
" off by", (((approx - exact) / exact) * 100).toFixed(1).padStart(5) + "%"
);
}
// The compound-growth pattern worth recognising on sight: 1.1 x 1.1 = 1.21.
console.log("");
const cagr = (start, end, years) => Math.pow(end / start, 1 / years) - 1;
console.log("$100M -> $121M over 2 years =>", (cagr(100, 121, 2) * 100).toFixed(1) + "% a year");
console.log("$100M -> $133M over 3 years =>", (cagr(100, 133, 3) * 100).toFixed(1) + "% a year");
console.log("$50M -> $200M over 6 years =>", (cagr(50, 200, 6) * 100).toFixed(1) + "% a year");
# Case math is estimation with an error bar you can state out loud, not exact
# calculation. Every shortcut below trades accuracy for speed -- so the useful
# thing to print is how much accuracy the shortcut actually cost.
import math
cases = [
["487 stores x $1.2M", 487 * 1.2e6, 500 * 1.2e6, "round 487 up to 500"],
["17% of $480M", 0.17 * 480e6, (0.1 + 0.05 + 0.02) * 480e6, "10% + 5% + 2%"],
["7 x $496", 7 * 496, 7 * 500 - 7 * 4, "distribute over (500 - 4)"],
["160 x 350", 160 * 350, 80 * 700, "halve one, double the other"],
["$500M / 150,000", 500e6 / 150000, 500000 / 150, "cancel three zeros each side"],
]
print("shortcut exact shortcut error")
for label, exact, quick, how in cases:
err = ((quick - exact) / exact) * 100
print(
" " + label.ljust(20),
f"{exact:.0f}".rjust(11),
f"{quick:.0f}".rjust(12),
(f"{err:.1f}" + "%").rjust(9),
" " + how,
)
# Rule of 72: something growing at r% a year doubles in roughly 72/r years.
print("")
print("rule of 72, against the exact doubling time")
for r in [4, 6, 8, 9, 12, 18]:
approx = 72 / r
exact = math.log(2) / math.log(1 + r / 100)
print(
" " + (str(r) + "%/yr").ljust(7),
"72/r =", f"{approx:.1f}".rjust(4), "yr ",
" exact =", f"{exact:.1f}".rjust(4), "yr ",
" off by", f"{(approx - exact) / exact * 100:.1f}".rjust(5) + "%",
)
# The compound-growth pattern worth recognising on sight: 1.1 x 1.1 = 1.21.
print("")
cagr = lambda start, end, years: (end / start) ** (1 / years) - 1
print("$100M -> $121M over 2 years =>", f"{cagr(100, 121, 2) * 100:.1f}" + "% a year")
print("$100M -> $133M over 3 years =>", f"{cagr(100, 133, 3) * 100:.1f}" + "% a year")
print("$50M -> $200M over 6 years =>", f"{cagr(50, 200, 6) * 100:.1f}" + "% a year")
shortcut exact shortcut error 487 stores x $1.2M 584400000 600000000 2.7% round 487 up to 500 17% of $480M 81600000 81600000 0.0% 10% + 5% + 2% 7 x $496 3472 3472 0.0% distribute over (500 - 4) 160 x 350 56000 56000 0.0% halve one, double the other $500M / 150,000 3333 3333 0.0% cancel three zeros each side rule of 72, against the exact doubling time 4%/yr 72/r = 18.0 yr exact = 17.7 yr off by 1.9% 6%/yr 72/r = 12.0 yr exact = 11.9 yr off by 0.9% 8%/yr 72/r = 9.0 yr exact = 9.0 yr off by -0.1% 9%/yr 72/r = 8.0 yr exact = 8.0 yr off by -0.5% 12%/yr 72/r = 6.0 yr exact = 6.1 yr off by -1.9% 18%/yr 72/r = 4.0 yr exact = 4.2 yr off by -4.5% $100M -> $121M over 2 years => 10.0% a year $100M -> $133M over 3 years => 10.0% a year $50M -> $200M over 6 years => 26.0% a year
Read the error column of the first table and something useful appears: four of the five shortcuts are exact. Decomposing a multiplication, halving-and-doubling, cancelling zeros and building a percentage from 10% and 1% do not approximate anything — they are identities. Only rounding trades accuracy, and here it cost 2.7%.
That is the argument for using all five aggressively. The techniques people avoid because they feel like cheating are the ones that lose nothing at all.
The rule of 72 is genuinely an approximation, and its error has a shape worth knowing: it is within about 2% for growth rates between roughly 4% and 12%, and degrades outside that band — 4.5% off at 18%. Since almost every growth rate in a case is single-digit, the rule is effectively exact in the range you will use it.
And the last three lines are the pattern to recognise instantly rather than compute. 1.1 × 1.1 = 1.21, so a jump from $100M to $121M over two years is 10% a year. Interviewers use round compound numbers constantly, and spotting one saves half a minute of arithmetic and, more importantly, saves you from producing a suspiciously precise 9.997%.
The formulas on one client
// One client, the formulas that cover most case arithmetic, and the single
// place candidates reliably put the wrong thing in the denominator.
const PRICE = 12; // $ per unit
const VAR_COST = 7; // $ per unit
const FIXED = 4_000_000; // $ per year
const VOLUME = 1_200_000; // units per year
const INVESTMENT = 18_000_000; // $ to launch the new line
const revenue = VOLUME * PRICE;
const totalCost = FIXED + VOLUME * VAR_COST;
const profit = revenue - totalCost;
const m = (x) => "$" + (x / 1e6).toFixed(1) + "M";
const row = (label, value) => console.log(" " + label.padEnd(22), String(value).padStart(10));
row("Revenue = V x P", m(revenue));
row("Total cost = F + V x c", m(totalCost));
row("Profit = R - TC", m(profit));
row("Profit margin = P/R", (profit / revenue * 100).toFixed(1) + "%");
row("ROI = profit/invest", (profit / INVESTMENT * 100).toFixed(1) + "%");
row("Payback = invest/profit", (INVESTMENT / profit).toFixed(1) + " yr");
// Breakeven volume. The denominator is CONTRIBUTION MARGIN PER UNIT -- price
// minus variable cost per unit -- and not total cost per unit.
const contribution = PRICE - VAR_COST;
const right = FIXED / contribution;
const wrong = FIXED / (totalCost / VOLUME);
console.log("");
console.log("breakeven volume, the right way and the usual way");
console.log(" F / (p - c) =", right.toFixed(0).padStart(9), "units <- correct");
console.log(" F / (TC/V) =", wrong.toFixed(0).padStart(9), "units <- wrong denominator");
console.log(" the error understates the requirement by",
((1 - wrong / right) * 100).toFixed(0) + "%,");
console.log(" which is the difference between advising a launch and advising against one.");
// Weighted average. Averaging the averages is the classic wrong move, and it
// is wrong by more the more unequal the weights are.
const segments = [
["urban", 0.28, 900],
["suburban", 0.18, 2600],
["rural", 0.06, 1500],
];
const naive = segments.reduce((a, s) => a + s[1], 0) / segments.length;
const weighted =
segments.reduce((a, s) => a + s[1] * s[2], 0) / segments.reduce((a, s) => a + s[2], 0);
console.log("");
console.log("blended margin across three segments");
for (const [name, margin, rev] of segments) {
console.log(
" " + name.padEnd(9),
(margin * 100).toFixed(0).padStart(3) + "% margin on",
("$" + rev + "M").padStart(7), "of revenue"
);
}
console.log(" average of the three margins :", (naive * 100).toFixed(1) + "%");
console.log(" revenue-weighted margin :", (weighted * 100).toFixed(1) + "%");
console.log(" quoting the first number overstates profit by",
m((naive - weighted) * segments.reduce((a, s) => a + s[2], 0) * 1e6));
# One client, the formulas that cover most case arithmetic, and the single
# place candidates reliably put the wrong thing in the denominator.
PRICE = 12 # $ per unit
VAR_COST = 7 # $ per unit
FIXED = 4_000_000 # $ per year
VOLUME = 1_200_000 # units per year
INVESTMENT = 18_000_000 # $ to launch the new line
revenue = VOLUME * PRICE
total_cost = FIXED + VOLUME * VAR_COST
profit = revenue - total_cost
m = lambda x: "$" + f"{x / 1e6:.1f}" + "M"
row = lambda label, value: print(" " + label.ljust(22), str(value).rjust(10))
row("Revenue = V x P", m(revenue))
row("Total cost = F + V x c", m(total_cost))
row("Profit = R - TC", m(profit))
row("Profit margin = P/R", f"{profit / revenue * 100:.1f}" + "%")
row("ROI = profit/invest", f"{profit / INVESTMENT * 100:.1f}" + "%")
row("Payback = invest/profit", f"{INVESTMENT / profit:.1f}" + " yr")
# Breakeven volume. The denominator is CONTRIBUTION MARGIN PER UNIT -- price
# minus variable cost per unit -- and not total cost per unit.
contribution = PRICE - VAR_COST
right = FIXED / contribution
wrong = FIXED / (total_cost / VOLUME)
print("")
print("breakeven volume, the right way and the usual way")
print(" F / (p - c) =", f"{right:.0f}".rjust(9), "units <- correct")
print(" F / (TC/V) =", f"{wrong:.0f}".rjust(9), "units <- wrong denominator")
print(" the error understates the requirement by",
f"{(1 - wrong / right) * 100:.0f}" + "%,")
print(" which is the difference between advising a launch and advising against one.")
# Weighted average. Averaging the averages is the classic wrong move, and it
# is wrong by more the more unequal the weights are.
segments = [
["urban", 0.28, 900],
["suburban", 0.18, 2600],
["rural", 0.06, 1500],
]
naive = sum(s[1] for s in segments) / len(segments)
weighted = sum(s[1] * s[2] for s in segments) / sum(s[2] for s in segments)
print("")
print("blended margin across three segments")
for name, margin, rev in segments:
print(
" " + name.ljust(9),
f"{margin * 100:.0f}".rjust(3) + "% margin on",
("$" + str(rev) + "M").rjust(7), "of revenue",
)
print(" average of the three margins :", f"{naive * 100:.1f}" + "%")
print(" revenue-weighted margin :", f"{weighted * 100:.1f}" + "%")
print(" quoting the first number overstates profit by",
m((naive - weighted) * sum(s[2] for s in segments) * 1e6))
Revenue = V x P $14.4M Total cost = F + V x c $12.4M Profit = R - TC $2.0M Profit margin = P/R 13.9% ROI = profit/invest 11.1% Payback = invest/profit 9.0 yr breakeven volume, the right way and the usual way F / (p - c) = 800000 units <- correct F / (TC/V) = 387097 units <- wrong denominator the error understates the requirement by 52%, which is the difference between advising a launch and advising against one. blended margin across three segments urban 28% margin on $900M of revenue suburban 18% margin on $2600M of revenue rural 6% margin on $1500M of revenue average of the three margins : 17.3% revenue-weighted margin : 16.2% quoting the first number overstates profit by $56.7M
Three things in that output are worth carrying.
The breakeven error is 52%, not 5%. Using total cost per unit in the denominator says the client needs 387,000 units to break even when they actually need 800,000. Current volume is 1.2 million, so both versions clear the bar here — but move volume to 500,000 and the two denominators give opposite recommendations from the same data. This is the arithmetic mistake most likely to end a case.
A 9-year payback is the interesting number, not the 11% ROI. They are the same fact stated twice — ROI = 1 / payback — and one of them lands. "Eleven percent return" sounds acceptable; "nine years to get the money back" invites the follow-up about whether the product line will even exist by then. Choose the framing that makes the decision visible.
Averaging the averages overstated the blended margin by 1.1 points, which is $56.7M. The naive average weights the tiny high-margin urban segment equally with the enormous low-margin suburban one. Whenever an interviewer hands you per-segment percentages, the weights are the point of the exhibit.
Saying it out loud, in four steps
Getting the number right is half of it. Practitioners who have interviewed at MBB describe the candidates who got offers as not the fastest but the clearest, and clarity here has a fixed shape.
1State the approach before you compute▾
To find the payback period, I'm going to divide the $18M investment by the annual incremental profit.
This costs six seconds and buys you a redirect if you were about to compute the wrong thing. An interviewer who sees you heading somewhere useless will usually stop you — but only if they know where you are heading.
2Compute out loud▾
$18M divided by $2M a year — that's 9 years.
Silence during arithmetic is the enemy. Not because thinking quietly is bad, but because if you make a small error in silence and then build three conclusions on it, the interviewer has to unwind all three. Narrated, they catch it at the source.
3Sanity-check, and say that you are doing it▾
Let me sanity-check that — $2M of profit on $14M of revenue is a mid-teens margin, which is high for grocery but plausible for a private-label line, so I'll go with it.
Roughly a third of candidates, by one Bain interviewer's account, present a clearly impossible number with confidence. A national coffee chain with $5,000 of annual revenue. Every American drinking 500 cups of coffee a day. The check takes five seconds and it is the single cheapest signal of discipline available in the round.
4Interpret, do not just report▾
A 9-year payback is long for a consumer product line, which suggests the client should look at options with faster returns before committing to this one.
This is the step that separates a candidate who can calculate from one who can consult. The number is an input to a decision; say what decision it moves.
The five mistakes
What to carry forward
You now have the three pieces the middle of a case is built from: a structure, the arithmetic to walk it, and a way to narrate both. The next module applies them to the four case archetypes that cover most of what gets asked — profitability, market entry and sizing, and M&A and pricing — each of which is really a specific tree with a specific first question.