Why shouldn’t you use Double for money, and what do you use instead?
Predict first
What does this print?
print(0.1 + 0.2 == 0.3, Decimal(string: "0.1")! + Decimal(string: "0.2")! == Decimal(string: "0.3")!)
- true true
- false true
- false false
Predict first
What does this print?
var d = Decimal(string: "2.675")!
var r = Decimal()
NSDecimalRound(&r, &d, 2, .plain)
print(String(format: "%.2f", 2.675), r)
- 2.68 2.68
- 2.67 2.68
- 2.67 2.67
Mental model. Doubles are measuring tape marked in halves: a third of a metre lands between the marks. Decimals are tape marked in tenths, the units money actually uses.
Double is a binary fraction, and most decimal fractions (0.1, 0.2) cannot be represented exactly in binary, so 0.1 + 0.2 == 0.3 is false. Errors are tiny, but money needs exact decimal results and well-defined rounding.
Use Decimal (a base-10 value type) or, better for storage and transport, integer minor units (cents, paise) with a currency code. Create decimals from strings or integers (Decimal(string: "0.1")), not from a Double literal, so you never depend on how a binary value was rounded. Round with an explicit rule and scale. Display through a currency format style, never by interpolating the number.
The doubles carry a tiny binary error; the decimals add exactly.
The double 2.675 is really 2.67499999…, so %.2f rounds down. The decimal is exactly 2.675 and rounds up with .plain.
Common traps
- Summing
Doubleprices in a loop. Error accumulates, and equality checks fail. - Rounding with
%.2f. It rounds the binary value, so 2.675 prints as 2.67.
They might follow up with
- Why integer cents on the wire? JSON numbers are parsed as
Doubleby many clients. Integers cross intact. - How do you split an amount three ways? Divide in minor units and distribute the leftover cent(s) explicitly.
Related questions
- 2.8.4 How do you format numbers, currency and percentages correctly?
- 2.5.3 Which JSONEncoder and JSONDecoder options matter beyond keys and dates?
← How do you format numbers, currency and percentages correctly? Which Foundation formatter replaces hand-written string code for… →