Calculating Percentage Discounts
Calculating a percentage discount sounds straightforward — until you realize that "30% off" can be computed two different ways, compound discounts do not simply add up, and rounding errors at scale turn into real money. Whether you are building an e-commerce checkout, writing pricing logic, or just trying to figure out the real price at a sale, understanding the mechanics of discount math prevents expensive mistakes.
The Two Methods: Multiply vs Subtract
Given a price of $80 and a 25% discount, most people compute the discount amount first, then subtract:
Subtract method:
- Discount amount = $80 × 0.25 = $20
- Sale price = $80 − $20 = $60
Multiply method:
- Sale price = $80 × (1 − 0.25) = $80 × 0.75 = $60
Both produce the same result for a single discount. The multiply method is preferable in code because it avoids an intermediate subtraction step and composes naturally when discounts stack:
function applyDiscount(price, discountPercent) {
return price * (1 - discountPercent / 100)
}
applyDiscount(80, 25) // 60
The subtract method becomes problematic when people try to combine discounts by adding the percentages. A 25% discount plus a 10% loyalty discount is not 35% off — it is a sequential application that yields a different result, which leads to the next section.
Compound Discounts
When multiple discounts apply, the order and method of combination matter.
Sequential discounts apply one after another:
function compoundDiscount(price, ...discounts) {
return discounts.reduce(
(p, d) => p * (1 - d / 100),
price
)
}
compoundDiscount(100, 25, 10) // 67.5, not 65
Step by step: $100 → 25% off = $75 → 10% off $75 = $67.50.
Many people mistakenly calculate $100 × (1 − 0.35) = $65. The difference of $2.50 per item becomes substantial at volume.
Why sequential application is called "stacking": Each subsequent discount applies to a smaller base, so the combined effect is always less than the sum of the individual percentages. The combined rate formula is:
Combined rate = 1 - (1 - d₁)(1 - d₂)(1 - d₃)...
For 25% + 10%:
Combined = 1 - (0.75 × 0.90) = 1 - 0.675 = 0.325 = 32.5%
Not 35%. The gap widens as percentages increase.
| Discounts | Sum of % | Actual Combined % | Difference |
|---|---|---|---|
| 10% + 10% | 20% | 19% | 1% |
| 25% + 10% | 35% | 32.5% | 2.5% |
| 50% + 50% | 100% | 75% | 25% |
| 50% + 30% + 20% | 100% | 72% | 28% |
That last row demonstrates why "100% off" via three stacked discounts is mathematically impossible — you always retain at least a fraction of the original price.
Common Retail Math Mistakes
Mistake 1: Adding discounts instead of multiplying. As shown above, this overestimates the discount. In regulated environments, advertising "35% off" when the actual combined discount is 32.5% can be a legal compliance issue.
Mistake 2: Rounding before multiplying. Rounding intermediate values in sequential discounts produces different final prices than rounding only at the end.
// Wrong: rounds after each step
const step1 = Math.round(100 * 0.75) // 75
const salePrice = Math.round(step1 * 0.90) // 68 (wrong!)
// Right: rounds only at the end
const salePrice = Math.round(100 * 0.75 * 0.90) // 68
// Actually same here, but with odd numbers:
const step1 = Math.round(99 * 0.75) // 74
const wrong = Math.round(step1 * 0.90) // 67
const right = Math.round(99 * 0.75 * 0.90) // 67
The discrepancy grows with more decimal places and more discount layers. Always round only the final result.
Mistake 3: Confusing markup with margin. A 50% markup on a $100 cost yields $150. A 50% margin on a $150 sale price means the cost was $75. These are different calculations:
// Markup: add to cost
const markupPrice = 100 * (1 + 0.50) // 150
// Margin: solve for price given margin requirement
const marginPrice = 100 / (1 - 0.50) // 200
Mistake 4: Percentage off vs percentage of. "30% off the original price" is different from "pay 30% of the original price." The first gives you a 30% discount; the second gives you a 70% discount. Ambiguous copy leads to customer complaints and refund requests.
Tax After Discount
In most jurisdictions, sales tax applies to the discounted price, not the original. The calculation sequence is:
function finalPrice(originalPrice, discountPercent, taxRate) {
const discounted = originalPrice * (1 - discountPercent / 100)
const withTax = discounted * (1 + taxRate / 100)
return Math.round(withTax * 100) / 100 // round to cents
}
finalPrice(80, 25, 8.25) // 64.95
// 80 × 0.75 = 60 → 60 × 1.0825 = 64.95
Always apply tax after discounts, not before. Taxing the pre-discount amount and then applying the discount violates tax regulations in most states.
Quickly verify any discount calculation using the percentage calculator at /tools/percentage-calculator, which handles percentage off, percentage change, and compound discount scenarios with step-by-step breakdowns.