Syllabus Lesson 70 of 141 · APIs nobody can misuse
APIs nobody can misuse

Money is not a float

Why it matters 0.1 + 0.2 is not 0.3 in binary floating point, and pounds plus euros is not a number at all. A money type that refuses both turns a class of silent accounting bugs into errors at the line that caused them.

A billing service passes amounts around as floats with a separate currency field. Two incidents this year: an invoice split three ways that came to a penny short, and a refund in euros added to a balance in pounds. You are writing the Money type the service will use instead.

Minor unit: the smallest coin of a currency: a penny for GBP, a cent for EUR and USD. JPY has none: amounts are whole yen.

Money("12.30", "GBP") + Money("0.70", "GBP")   # Money("13.00", "GBP")
Money("100.00", "GBP").split(3)                # 33.34, 33.33, 33.33
Your turn

Write class Money.

  • Money(amount, currency): amount is a str like "12.30", an int (whole units) or a decimal.Decimal; currency is "GBP", "EUR", "USD" (2 decimal places) or "JPY" (0).
  • Support + and - between Money, * by an int (either side), ==, <, <=, >, >= and str() ("GBP 12.30", "JPY 500").
  • split(n): n parts that add up exactly, larger first, differing by at most one minor unit.

Hidden misuse scripts will try these:

  • TypeError: a float or bool amount; Money plus or minus a non-Money; multiplying by anything but an int.
  • ValueError: an unknown currency (codes are upper case); an amount with more places than the currency has, or that is not a number; adding, subtracting or ordering two currencies; split(n) with n below 1.
  • Money equals only Money of the same currency and amount (anything else is simply not equal).

Spotted a problem in this lesson? Report it · Ask about this lesson

This exercise opens soon

Finish the lesson before it and this one unlocks. The whole course is free, no account needed, and you can read this lesson right now.