Calculating Percentage Discounts

May 28, 20266 min read

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:

  1. Discount amount = $80 × 0.25 = $20
  2. Sale price = $80 − $20 = $60

Multiply method:

  1. 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.

DiscountsSum 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.