DEV Community

Prime Voucher
Prime Voucher

Posted on

The Maths Behind 10 Shopping Calculators (with JavaScript Snippets)

Shopping maths looks trivial until you try to code it. "20% off, then an extra 10% with a voucher" is not 30% off. Removing VAT is not "minus 20%". And "free delivery over £50" can make you spend more than you save.

I recently helped build a set of free UK shopping calculators for PrimeVoucher, and here are the formulas behind each one, in plain JavaScript you can drop into your own project.

1. Basic discount

const salePrice = (price, pct) => price * (1 - pct / 100);
const saving = (price, pct) => price * (pct / 100);

salePrice(80, 25); // 60
Enter fullscreen mode Exit fullscreen mode

Try it live: Discount Calculator

2. Stacked discounts (sale + voucher code)

The classic bug: adding percentages. Discounts multiply, they don't add.

const stacked = (price, ...pcts) =>
  pcts.reduce((p, pct) => p * (1 - pct / 100), price);

const realPercentOff = (...pcts) =>
  100 * (1 - pcts.reduce((m, pct) => m * (1 - pct / 100), 1));

realPercentOff(20, 10); // 28, not 30
Enter fullscreen mode Exit fullscreen mode

Live version: Stacked Discount Calculator

3. "% off" vs "£ off" - which voucher wins?

A percentage code beats a fixed-amount code once the basket passes a break-even point:

const breakEven = (poundsOff, pctOff) => poundsOff / (pctOff / 100);

breakEven(10, 15); // £66.67 - above this, 15% off wins
Enter fullscreen mode Exit fullscreen mode

Compare two codes: Voucher Comparison Calculator

4. Free delivery threshold

Is it worth adding an item to hit free shipping? Only if what you add is something you'd buy anyway, or costs less than delivery.

const shortfall = (basket, threshold) => Math.max(0, threshold - basket);
const worthTopUp = (basket, threshold, deliveryFee) =>
  shortfall(basket, threshold) <= deliveryFee;

worthTopUp(42, 50, 4.99); // false - £8 top-up costs more than £4.99 delivery
Enter fullscreen mode Exit fullscreen mode

Free Delivery Calculator

5. UK VAT (add and remove)

Removing VAT means dividing by 1.2, not subtracting 20%.

const addVat = (net, rate = 20) => net * (1 + rate / 100);
const removeVat = (gross, rate = 20) => gross / (1 + rate / 100);

removeVat(120); // 100 (not 96!)
Enter fullscreen mode Exit fullscreen mode

VAT Calculator

6. Price per unit

Normalise everything to the same unit before comparing multipacks.

const perUnit = (price, qty, unitSize = 1) => price / (qty * unitSize);

perUnit(3.5, 6, 330) * 100;  // pence per 100ml for a 6x330ml pack
Enter fullscreen mode Exit fullscreen mode

Price Per Unit Calculator

7. Voucher + cashback + gift card

Order matters: the voucher comes off first, and cashback is usually paid on the post-voucher amount (excluding delivery).

const realCost = ({ price, voucherPct = 0, cashbackPct = 0, giftCardDiscountPct = 0 }) => {
  const afterVoucher = price * (1 - voucherPct / 100);
  const cashback = afterVoucher * (cashbackPct / 100);
  const paid = afterVoucher * (1 - giftCardDiscountPct / 100);
  return paid - cashback;
};
Enter fullscreen mode Exit fullscreen mode

Cashback & Voucher Calculator

8. Pay in 3 / Buy Now Pay Later

Split evenly, but put the rounding remainder on the first instalment so the total always adds up to the penny.

const instalments = (total, n) => {
  const pence = Math.round(total * 100);
  const base = Math.floor(pence / n);
  const first = pence - base * (n - 1);
  return [first, ...Array(n - 1).fill(base)].map(p => p / 100);
};

instalments(100, 3); // [33.34, 33.33, 33.33]
Enter fullscreen mode Exit fullscreen mode

Pay in 3 Calculator

9. Sale savings tracker

const totalSaved = items =>
  items.reduce((s, { was, now }) => s + (was - now), 0);
Enter fullscreen mode Exit fullscreen mode

Sale Savings Calculator

10. Annual savings

const yearly = (avgSavingPerOrder, ordersPerMonth) =>
  avgSavingPerOrder * ordersPerMonth * 12;
Enter fullscreen mode Exit fullscreen mode

Annual Savings Calculator

A note on money and floating point

0.1 + 0.2 !== 0.3 in JavaScript. For anything shown to users, either work in integer pence (as in the Pay in 3 example) or round at the end with Math.round(x * 100) / 100. Display with Intl.NumberFormat('en-GB', { style: 'currency', currency: 'GBP' }).


All ten calculators are free to use at primevoucher.co.uk/calculators. If you spot an edge case I've missed, drop it in the comments.

Top comments (0)