Why shouldn’t you use Double for money, and what do you use instead?

2.8.5 · L2 · Interview

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 Double prices 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 Double by 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… →