Do these 3 things before closing this tab:
1Scan for outdated or missing drivers - takes under a minute2Clear out junk files and repair common Windows errors3Fix the driver behind crashes, sound loss and screen glitchesiTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more
A discount calculator needs three pieces of arithmetic: the sale price is the original price multiplied by (1 − d/100), the savings are the original price minus the sale price, and the original price can be recovered by dividing the sale price by (1 − d/100). Stacked discounts multiply rather than add. The JavaScript below implements those rules, validates inputs before calculating, and formats the output for a locale and currency.
The headline’s $2,400 is the author’s own savings estimate. It is a personal claim, not a measured figure, and the code can only compute prices; it cannot show how much anyone actually saved.
The core formula
Sale price
For an original price P and a discount percentage d, the sale price is:
salePrice = P × (1 − d / 100)
A 25% discount on a $80 item gives 80 × (1 − 0.25) = 60. The percentage is divided by 100 first, so you never multiply by the raw number 25.
#1 Best Overall
Savings
The amount saved is the difference between the two prices:
savings = P − salePrice
For the same $80 item at 25% off, savings are $20. Because savings is computed from the sale price, the two results always agree and you only need to validate the inputs once.
Working backwards from a sale price
When a shelf or checkout shows only the discounted price and the percentage, divide by the fraction that remains:
Rank #2
originalPrice = salePrice / (1 − d / 100)
If $60 is the sale price after 25% off, the original is 60 ÷ 0.75 = 80. This formula only works when the discount is below 100%. At exactly 100% the divisor is zero, and above 100% the result is meaningless, so the code below rejects both.
Stacked discounts: multiply, don’t add
Two percentage discounts applied one after another act on the running price. The combined formula is:
final = price × (1 − discount1) × (1 − discount2)
Take a $100 item with 20% off and then an additional 10% off. The table shows how each step works.
| Step | Discount applied | Multiplier | Running price |
|---|---|---|---|
| Start | none | 1.00 | 100.00 |
| First discount | 20% | 0.80 | 80.00 |
| Second discount | 10% (applied to 80.00) | 0.90 | 72.00 |
The final price is 72, so the combined reduction is 28%, not 30%. Adding the two percentages would wrongly suggest a $70 price. The gap grows with larger discounts: 50% followed by 50% leaves 25% of the original, a 75% reduction rather than 100%.
Free tools Windows power users keep installed
One-click scans. No signup required.
Implementing the calculator
Keep three stages separate: parse and validate input, calculate with plain numbers, and format the result only when it is shown. Mixing them is the usual source of bugs, because form fields deliver text and text does not behave like a number in arithmetic.
Parse and validate input
Form values are strings. Converting an empty field with Number("") returns 0, which would silently produce a free item, so check for blank input before converting. Then confirm the result is a finite number. MDN documents Number.isFinite() as returning true only for finite values of type number, and false for NaN, positive or negative infinity, and non-number inputs. It does not coerce strings, so the parsing step must come first.
Rank #4
function parseNumber(text) {
const trimmed = String(text).trim();
if (trimmed === "") return NaN;
return Number(trimmed);
}
Calculate with numbers only
function salePrice(original, percentOff) {
if (!Number.isFinite(original) || !Number.isFinite(percentOff)) {
throw new RangeError("Inputs must be finite numbers.");
}
if (original < 0 || percentOff < 0 || percentOff > 100) {
throw new RangeError("Price must be zero or more; discount must be 0 to 100.");
}
return original * (1 - percentOff / 100);
}
function savings(original, percentOff) {
return original - salePrice(original, percentOff);
}
function originalFromSale(sale, percentOff) {
if (!Number.isFinite(sale) || !Number.isFinite(percentOff)) {
throw new RangeError("Inputs must be finite numbers.");
}
if (percentOff <= 0 || percentOff >= 100) {
throw new RangeError("Discount must be above 0 and below 100 to reverse.");
}
return sale / (1 - percentOff / 100);
}
function stackDiscounts(price, ...percents) {
return percents.reduce((current, d) => salePrice(current, d), price);
}
Calling stackDiscounts(100, 20, 10) returns 72. Each discount is validated against the running price, so the same range checks apply at every step.
Format for display
Use Intl.NumberFormat with an explicit locale and currency code. MDN documents it as the locale-sensitive way to produce number and currency output, and it is preferable to concatenating a symbol onto a number, because separators, symbol placement, and fraction digits vary by locale.
Crashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minuteWindows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstallfunction formatMoney(amount, locale, currency) {
return new Intl.NumberFormat(locale, { style: "currency", currency }).format(amount);
}
formatMoney(72, "en-US", "USD"); // "$72.00"
When no fraction-digit options are given, the default follows the currency’s minor-unit count from ISO 4217. Two digits is typical for USD and EUR; a currency with zero minor units, such as JPY, displays without decimals. Choose the locale based on where the shopper is, not on the code’s default.
Best Value
Rounding is a policy decision
Formatting to two decimal places controls what the screen shows; it does not define how a store must round. Whether a merchant rounds each line item, the total, or tax, and in which direction, is set by its own checkout logic and by local rules. The MDN references above establish how the formatter behaves, not what any retailer or jurisdiction requires. If your calculator is meant to match a real bill, confirm the rounding method with the merchant or the relevant tax authority before relying on it.
Common failure modes
- Blank field treated as zero. Handle empty strings before calling
Number(), as shown above. - Discount above 100%. The sale price would be negative, and the reverse formula would divide by zero or a negative number. The validation rejects it.
- Adding stacked discounts. 20% plus 10% is 28% of the original, not 30%.
- Formatting before calculating. A string like “$72.00” cannot be used in further arithmetic, so keep the raw number until the last step.
- Hard-coded currency symbol. Pass the locale and currency code to
Intl.NumberFormatinstead.
Where to read the primary sources
The arithmetic here is standard percentage math, and the worked examples can be checked against the online discount calculator at mathcheck.net, which covers sale price, stacked discounts, and reverse calculation. For the JavaScript behaviour, read the MDN reference for Number.isFinite() and the Intl.NumberFormat() constructor. Percentage-discount price examples also appear in the CBSE Academic Class XI JavaScript chapter, available at cbseacademic.nic.in.
Keep the savings claim in perspective
The calculator tells you what a discount is worth on a given price. Whether it saved $2,400 over a year depends on which purchases the author would otherwise have made, and no independent measurement of that figure is available. Treat the number as the author’s own estimate and use the functions above to check any individual deal.
Recommended Free Tools
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

