Money Bugs: Rounding, Tax, Currency, and Refunds

Written By  Crosscheck Team

Content Team

July 18, 2026 9 minutes

Money Bugs: Rounding, Tax, Currency, and Refunds

Money bugs: rounding, tax, currency, and refunds

A customer sends support a screenshot. Her invoice says $107.63. Her card statement says $107.64. That single cent now costs an hour of support time, an afternoon of engineering, and a note in the finance team's monthly reconciliation.

Most bugs annoy people. Money bugs also create legal and accounting problems, which is why testing currency calculations deserves its own test pass.

Short version

  • Computers cannot store 0.10 exactly, so 0.1 + 0.2 gives 0.30000000000000004.
  • Use values that break rounding: 1.005, 2.675, 0.145.
  • Ask where rounding happens — per line or on the total. The two answers differ.
  • Split a $100.00 invoice three ways and look for the missing cent.
  • Not every currency has two decimal places. JPY has none. KWD has three.
  • Refunds are where money bugs become real money lost. Test double refunds and over-refunds.

The floating point trap

Floating point is the way most programming languages store numbers with a decimal point. It stores them in binary, using ones and zeros.

Think about how you write one third as a decimal: 0.333..., and it never ends. You have to stop somewhere, so you write 0.3333 and accept a tiny error. Binary has the same problem with 0.1. It cannot be written exactly, so the computer stores something extremely close instead.

Those tiny errors add up. In a browser console, type this:

0.1 + 0.2
// 0.30000000000000004

That is not a bug in your product. That is how the number type works. It becomes a bug when nobody handles it.

The two correct fixes

  1. Store amounts as integer cents. Instead of 19.99, the system stores 1999 — a whole number of the smallest unit. Whole numbers have no rounding error. Convert to dollars only when showing the value on screen.
  2. Use a decimal library. A decimal library is code that handles decimal numbers exactly, the way a person does on paper, rather than in binary.

How you spot the symptom as a tester

  • The same total reads $19.99 on the cart page and $20.00 on the confirmation page.
  • A value like 1.005 rounds down to 1.00 when it should round up to 1.01.
  • A total appears as 0.30000000000000004 or 19.989999999999998 in an API response, even though the screen looks fine.
  • A line total ends in a third decimal place, such as $99.999.

Always check the raw API response, not only the screen. The screen often rounds a broken number into a correct-looking one.


Rounding rules

There is more than one way to round, and teams often disagree without knowing it.

Round half up is what most people learn at school. A half always goes up, so 2.675 becomes 2.68.

Banker's rounding, also called round half to even, sends a half to the nearest even digit. So 2.665 becomes 2.66, and 2.675 becomes 2.68. Banks use it because it stops a long list of amounts from drifting upward.

Neither is wrong. Two different rules inside one product is wrong.

Where you round matters just as much. Take three lines of $10.005:

  • Round each line first: 10.01 + 10.01 + 10.01 = 30.03
  • Add first, then round the total: 30.015 rounds to 30.02

Both are defensible. They differ by a cent. If the cart rounds per line and the invoice rounds the total, your customer sees two numbers for one order.

Ask your developer one question before you start: "Do we round each line or the total, and which rounding rule do we use?" Write the answer in the test plan. Then check that the cart, the checkout, the invoice PDF, the emailed receipt, and the payment provider all agree.


Tax and discounts

Tax and discounts are where rounding errors get multiplied.

Tax on an ordinary amount. Enter 19.99 with tax at 8.25%. The tax is 1.649175. Rounded, it is 1.65, and the total is 21.64. A system that cuts the number off instead of rounding it shows 21.63.

Order of operations. A 10% discount and 8.25% tax on $50.00 can be applied two ways: discount first, then tax, or tax first, then discount. The final totals can differ by a cent, and in many places the law says which order is correct. Test the order your finance team specifies, not the one that looks neat.

Small amounts. Apply a 10% discount to $0.05. The discount is 0.005 — half a cent. Does your system give a $0.01 discount or $0.00? Either answer can be right. Silently charging $0.045 is not.

Splitting. Split a $100.00 invoice three ways. 33.33 x 3 = 99.99. One cent is missing. A correct system assigns the extra cent to one share, usually the first or the last, so the parts still add to 100.00. Look at what your product does with that cent, and check that the same cent is not assigned twice.

Unit prices with more decimals. Order quantity 3 at $33.333 each. The true line total is 99.999. Does it show 100.00, 99.99, or 99.999? Then add tax to that line and see whether the rounded or unrounded figure was used.


Currency and display

Two decimal places is a habit, not a rule.

  • Zero decimal places: Japanese yen (JPY) and Korean won (KRW). ¥1000.00 is wrong. It should be ¥1,000.
  • Three decimal places: Kuwaiti dinar (KWD) and Bahraini dinar (BHD). KD 5.25 should be KD 5.250.

Minor units are the smallest piece of a currency — cents for dollars. Payment providers such as Stripe take amounts in minor units, so 1000 means $10.00. For JPY there are no minor units, so 1000 means ¥1,000. Send 1000 when you meant ¥10 and the customer pays a hundred times too much. Always check the amount your product sends to the payment provider, not just the amount on screen.

Separators change by locale. A locale is the language and region setting that decides how numbers and dates are written. In the United States you write 1,234.56. In Germany you write 1.234,56. Switch the language and confirm the value changes correctly. A 1.234,56 read as 1.234 is a bug worth over a thousand units.

Negative amounts appear as -$5.00 in most software and ($5.00) in accounting documents. Pick one and check refunds, credit notes, and exported reports all use it.

Exchange rates need their own rule. If $1 = €0.9163, then $19.99 is €18.316.... Check when the rate is captured, how the result is rounded, and whether converting back gives the original amount. It usually does not, and your product should not pretend otherwise.


Refunds and partial refunds

Refunds move real money out. Test them hardest.

  1. Refund more than was paid. Pay $50.00, then request $50.01. The request must be rejected with a clear message.
  2. Refund twice. Refund the full amount, then send the same refund again. Stripe answers with the error code charge_already_refunded. Your product must show that as a handled message, not a blank page.
  3. Partial refund of a taxed line. Refund one item from a taxed order. Confirm the tax is refunded in proportion, and that the remaining order total still matches the remaining lines.
  4. Refund a discounted item. If a customer bought three items with a "buy two get 10% off" discount and returns one, they should not get the full sticker price back.
  5. Refund in the wrong currency. A payment in EUR must be refunded in EUR. Confirm the product never converts a refund at today's rate.
  6. Refund after a partial refund. Refund $20.00 of a $50.00 charge, then try to refund $40.00. Only $30.00 remains.

When a money bug appears, capture the exact request and response, because the screen alone rarely shows where the cent went. Tools such as Crosscheck record the network requests and console output alongside the screenshot, so the failing payload is already attached to the report.


The exact values to type

Use this list every time you test money. It takes five minutes and finds most rounding bugs.

Type 0.1, 0.2, 0.005, 1.005, 2.675, 0.145, 9999999.99, -1, 100.00 split by three, 19.99 with 8.25% tax, and quantity 3 at $33.333.

Value you enterWhat a buggy system showsWhat is correct
0.1 + 0.20.300000000000000040.30
1.005 rounded to 2 places1.001.01
2.675 rounded to 2 places2.672.68
0.145 rounded to 2 places0.140.15
19.99 with 8.25% tax21.6321.64
100.00 split three ways33.33, 33.33, 33.33 (sums to 99.99)33.34, 33.33, 33.33
Quantity 3 at $33.33399.999 on the invoice100.00, rounded once by an agreed rule
10% off $0.05$0.045 charged$0.04 or $0.05, by a written rule
9999999.991e+07 or a silent overflow9,999,999.99
Quantity -1A -$19.99 credit on the orderRejected with a validation message

Frequently asked questions

Why does 0.1 + 0.2 not equal 0.3? Binary cannot store 0.1 exactly, in the same way decimal cannot store one third exactly. The tiny stored error shows up when you add the numbers.

Should I file a bug for a one-cent difference? Yes. One cent means the calculation rule is inconsistent somewhere, and the same fault will produce larger gaps on larger orders.

Which rounding rule is correct for my product? There is no universal answer, so ask finance and write the rule down. Your job as a tester is to prove every screen and every export follows the same one.

Do I need to test currencies my company does not sell in yet? Test at least one zero-decimal currency such as JPY and one three-decimal currency such as KWD. They expose formatting assumptions that break the day someone adds a new market.

Where should I check the real numbers? In the API response and in the payment provider dashboard, not only in the user interface. The interface formats values for display, which can hide a wrong number underneath.

Related Articles

Contact us
to find out how this model can streamline your business!

Trusted by thousands ofengineering teams worldwide.

Add to Chrome
200+ reviews · 100k+ users
Crosscheck browser extension capture controls

Join the Crosscheck Community

Stay in the loop with Crosscheck's newest features and insights.