Money & Currency Handling
We move and account for money, so the arithmetic must be exactly right. A fraction of a penny in the wrong type, rounding at the wrong moment, or two currencies added together can turn into real financial errors and hard reconciliation problems. Money has rules. Follow them precisely.
The first rule, and the one most often broken by people new to financial code: never use floating point (double or float) for money. Binary floating point cannot represent values like 0.10 exactly, so amounts drift. Use a decimal type, store the currency next to the amount, and decide on purpose when and how rounding happens.
Money is really three things that travel together: an amount, a currency, and a scale (how many decimal places). Treat them as one unit. Never mix currencies silently. Make rounding an explicit, documented choice, not an accident of the type system.
Represent money correctly
- AlwaysUse an exact decimal type for monetary amounts (C#
decimal, SQLDECIMAL), neverdoubleorfloat. - DoAlways pair an amount with its currency, and store and transport them together. An amount without a currency is meaningless and dangerous.
- DoUse the correct scale per currency (most have 2 decimal places; some have 0 or 3), and keep enough precision for intermediate calculations.
- ConsiderA dedicated Money type (amount plus currency), so the compiler stops you mixing currencies, instead of relying on bare decimals.
- NeverAdd, compare, or combine amounts in different currencies as if they were the same number. Convert them explicitly with a known rate first.
double total = 0.1 + 0.2; // 0.30000000000000004
if (total == 0.3) { ... } // never true
Floating point cannot represent these values exactly, so totals drift and comparisons fail. Over many transactions this becomes real money lost and accounts that will not reconcile.
decimal total = 0.1m + 0.2m; // exactly 0.3
var charged = Math.Round(total, 2, MidpointRounding.ToEven);
Decimal is exact, and rounding to the charged scale is one explicit, documented step.
Calculate and round deliberately
- DoDecide rounding explicitly: when it happens, to what scale, and which rounding mode (for example, banker's rounding). Then apply it consistently.
- DoRound once, at the right point (for example, on the final charged total), not repeatedly mid-calculation where errors build up.
- DoWhen splitting or allocating money (for example, dividing a total across lines), make sure the parts add back up to the whole. Handle the leftover penny on purpose.
- ConsiderRecording the exchange rate (and its source and time) with any converted amount, for audit and reconciliation (see Audit Trails).
- AvoidDoing money maths in the database and the application in different ways. Pick one source of truth for a calculation, so results cannot diverge.
Self-review checklist
- AskIs every monetary value a decimal, never a float or double?
- AskDoes every amount carry its currency, and am I ever mixing currencies?
- AskWhere exactly does rounding happen, to what scale, and in which mode, and is it done only once?
- AskWhen I split a total, do the parts add back up to it?