SafeSale · Blog

Sale prices ending in .99: rounding a Shopify discount

Why 20% off leaves prices like 27.60, how Shopify rounds percentage discounts per unit, and what a .99, .95 or whole-number ending does to the discount.

Suppose a small ceramics shop runs 15% off everything. Its prices are tidy: 24.00 for a mug, 36.00 for a bowl, 58.00 for a planter. Fifteen percent off turns them into 20.40, 30.60 and 49.30. Nothing is wrong with those numbers. They are just not numbers anyone would choose, and for two weeks every product page, collection grid and ad will show them.

Rounding fixes that, but it also changes how much you are giving away, and the two sale mechanisms on Shopify handle it differently. This post goes through where the odd cents come from, what each ending does to the discount, and how SafeSale applies a rounding rule in both a price rewrite and a checkout discount.

Where the odd cents come from

A percentage of a price can produce a fraction of a cent. 20% off 24.99 is 4.998 off, 15% off 36.00 is 5.40 off, 12.5% off 29.99 is 3.74875 off. Something has to decide what happens to the fraction, and Shopify and a spreadsheet do not always agree.

In February 2026 Shopify confirmed on its developer forum that percentage discounts are now calculated per unit, with the discount rounded down to the cent, on every store. Their example: a 46.99 item at 10% off gets 4.69 taken off, not 4.70, so the unit price is 42.30 and two of them cost 84.60. Before the change the discount was computed on the line total, so the same two mugs came to 84.59.

So percentage sales without an ending rule can produce prices like 27.60, 10.20 and 42.30. They are correct to the cent and they look like a calculator wrote them, because one did.

Three endings and what they cost

SafeSale offers four choices on every rule: no rounding, round to .99, round to .95, or round to a whole amount. The first three never make a price higher than the un-rounded result; the fourth can. Here is the ceramics shop at 15% off under each setting.

15% off, four rounding settings
OriginalNo rounding.99.95Whole
24.0020.4019.99 (16.7% off)19.95 (16.9%)20.00 (16.7%)
36.0030.6029.99 (16.7%)29.95 (16.8%)31.00 (13.9%)
58.0049.3048.99 (15.5%)48.95 (15.6%)49.00 (15.5%)

Two things stand out. The .99 and .95 endings can deepen the discount by up to 99 cents per unit, and the amount depends entirely on where the un-rounded price happened to fall. With .99 rounding, the bowl gives away an extra 61 cents; the planter 31. A price already ending in .99 stays there. There is no useful catalogue-wide average without looking at your prices and sales mix, especially when low-priced items make up much of it.

Whole-number rounding goes the other way. It rounds half-up, so the bowl at 30.60 becomes 31.00 and the discount on it shrinks from 15% to 13.9%. If a rule would ever push a price above the original (a tiny percentage on a price just under a whole number), SafeSale blocks that variant in the preview rather than raising its price.

Cheap items need a look of their own. Rounding to .99 on a 1.20 item at 15% off produces 0.99, which is 17.5% off rather than 15%. Below 0.99 SafeSale leaves the computed price alone under .99 rounding, since there is no positive .99 price to snap down to. Where cents matter most, the preview’s per-variant percentage column is the thing to read, and the variant-by-variant preview exists for exactly this kind of check.

How the rounded price reaches the shopper

The two ways of running a sale treat a rounded price very differently.

Price rewrite. SafeSale computes the rounded price from the snapshot, writes it into the variant’s price field and sets compare-at to the original. The theme shows 19.99 struck against 24.00 on the product page, the collection grid, the cart and checkout, because 19.99 is now simply the price. Themes that display a “save X%” badge compute it from those two numbers and round it themselves, so a 15% sale with .99 rounding may be labelled 17% off on one product and 16% on the next. If the campaign says 15%, consider a badge with a fixed label instead of a computed percentage.

Checkout discount (Functions mode). Stored prices do not change, so there is no price field to round. Instead SafeSale’s discount function runs on each cart line. With a plain percentage and no rounding, it hands Shopify the percentage and Shopify does the per-unit arithmetic described above. The moment a rounding rule is attached, the function computes the rounded unit price itself from the line’s current price and returns a fixed amount off per item, so the mug reaches 19.99 per unit in this same-currency example, before any other combined discounts or taxes.

That difference has a practical consequence. SafeSale’s preview rounds the percentage half-up, the way most people and most spreadsheets do; Shopify rounds a plain percentage down. On most prices the two agree. On a price like 46.99 at 10% they differ by a cent: the preview says 42.29, checkout charges 42.30. If you need the preview and the checkout to match to the cent in Functions mode, pick any rounding setting, including .99, and the function sends an exact per-unit amount instead of leaving the arithmetic to Shopify.

What rounding does not survive

In price rewrite mode, rounding is applied to the price in your store currency. When Shopify Markets converts that price for another country, the ending is whatever the exchange rate produces, unless the market has Shopify’s own rounding rules switched on. A 19.99 at home can be a 23.17 abroad. Functions mode calculates from the cart line's current unit price instead, so test each market rather than treating a store-currency preview as its checkout total. The Markets post covers which mechanism gets you a tidy ending in each currency.

Rounding also makes the end of the sale harder to compute and easier to get wrong. 26.99 divided by 0.8 is 33.74, not the 34.50 the bowl cost before. An app that “restores” by reversing the percentage will leave every rounded variant a few cents off, which is the problem the snapshot post is about. SafeSale ends a rewrite sale by writing back the recorded pre-sale price and compare-at, so rounding costs nothing at the end. In Functions mode there is nothing to restore; the discount stops and the stored prices were never touched.

Choosing a setting

  • If your regular prices already end in .99 or .95, round the sale to the same ending. A shop whose prices end in .00 looks odd with a sale full of .99s, and the reverse is true too.
  • Prefer a fixed sale price over a percentage when a handful of hero products need a specific number. SafeSale’s per-variant overrides let a 15% sale carry a 49.00 planter without a second sale.
  • Treat whole-number rounding as a different offer, since it can reduce the discount on some variants. If the campaign is advertised as 15% off, stick to .99 or .95, which only ever go deeper.
  • Check the preview’s maximum discount after turning rounding on. A 49.5% rule rounded to .99 can tip a cheap variant past the 50% confirmation threshold, and SafeSale will ask you to type the sale name before it runs.
  • Ask whether the campaign is sold on the percentage or on the price. “15% off” needs no rounding and reads fine with 20.40. “Mugs from 19.99” needs a rounding rule, or a fixed price.

A sale’s rounding rule is a small setting with a measurable cost: a few cents a unit in one direction, or a shallower discount in the other. SafeSale shows the exact result and the exact percentage for every variant before anything is written, in either mode. Rewrite sales restore the original prices from a snapshot when they end; Functions sales leave stored prices alone.

Frequently asked questions

Does rounding to .99 ever make the discount smaller than the percentage I set?

Not in SafeSale. The .99 and .95 endings only move a price down to the nearest matching ending, never up, so 20% off 34.50 becomes 26.99 rather than 27.99. Whole-number rounding is the exception: it rounds half-up, so 27.60 becomes 28.00 and the effective discount on that variant drops to 18.8%. The preview shows the real percentage for every variant either way.

Why does my checkout total differ by a cent from the sale price I calculated?

Shopify works out a percentage discount per unit and rounds the discount down to the cent; a spreadsheet using half-up rounding can differ. On 46.99 at 10% off, Shopify takes 4.69 off and charges 42.30, while half-up rounding gives 42.29. In Functions mode, adding a rounding rule makes SafeSale send a fixed discount amount per unit instead of a percentage. That removes this particular rounding difference; currency conversion and other combined discounts still need checking.

Can I round sale prices with Shopify's own automatic discount?

An automatic percentage discount in the Shopify admin has no rounding option; the shopper pays whatever the arithmetic produces. To end sale prices in .99 without an app you would have to set a fixed price per variant, by hand or by CSV, and put it back afterwards. That is the work a sale app's rounding rule replaces.

What happens to rounded prices when the sale ends?

In price rewrite mode, SafeSale restores the snapshot taken before the sale, including the original price and compare-at price. A rounded discount cannot be undone by reversing the percentage: 26.99 divided by 0.8 is 33.74, not the 34.50 you started with. In Functions mode the discount stops; stored prices were never changed, so there is nothing to restore.