The idea: money is a whole number of cents plus a currency

A huge share of all software adds, splits and taxes money, yet few languages have a money type. Programs fall back on a double or a bare number, and two problems follow. One is currencies: nothing stops you from adding dollars to yen. The other, harder to see, is rounding: amounts must end up in whole cents, and careless rounding loses or creates pennies. Martin Fowler's Money pattern, from Patterns of Enterprise Application Architecture (2002), fixes both with one small class.

Left: the Money class with two fields, long amount in minor units and Currency currency, and its operations: add and subtract for one currency only, multiply with a rounding mode, allocate, equals and hashCode, and no setters. Right: 0.10 stored as a double is 0.1000000000000000055 and so on, while 10 stored as a long of cents is exactly ten cents.
Money keeps an exact integer count of the smallest unit next to its currency; a double can only keep the nearest binary fraction.

A Money object has two fields: the amount, a long counting the smallest unit of the currency (cents for dollars and euros, yen for yen), and the currency. Its operations know about both. The four tabs of the canvas each show one part: the double that drifts, Money as a value, splitting a sum, and tax rounding. The right side always shows the Java code that runs, with the current line lit, and a check panel with the locals and whether the cents still add up.

Why not a double

The book opens its Java example with a three-line test: start a double at 0.00, add 0.10 ten times, and print whether the result equals 1.00. It prints false. The first tab runs that loop step by step. After every += it shows the 64 bits of val, its exact decimal value and its drift from i/10. The third sum already prints as 0.30000000000000004; the fifth is exactly 0.5 again; the tenth is 0.9999999999999999, one representable step (2−53) below 1.0 (Demo 1: float drift).

The same loop on a long that counts cents adds 10 ten times and gets exactly 100 (Demo 2: cents vs double). Why 0.1 has no exact binary form, and how the sign, exponent and fraction of a double work, is the subject of Floating Point and Number Representation; the narrower formats are on FP32, FP16, BF16, FP8.

Minor units and centFactor

The amount can be an integer or a fixed-point decimal (BigDecimal), never a floating-point type. The book's class uses a long of minor units, because integer math is exact and reads well. Each currency has a number of fraction digits (2 for USD and EUR, 0 for JPY), so centFactor() is 10 to that power: 100 or 1. A double from outside is converted once, at the edge, with Math.round(amount × centFactor()); from then on every calculation is on the integer. Try Money(500, JPY) and Money(5.00, USD) in the Money values tab: both store the integer 500, with different currencies.

A long of cents overflows past 9,223,372,036,854,775,807 cents, about 92 quadrillion dollars. Code that might go beyond that, or that needs fractions of a cent (interest rates, fuel prices), uses BigInteger or BigDecimal, and should use a different type for fractional money, because it behaves differently under arithmetic.

Money is a Value Object

Fowler separates reference objects, whose equality is identity (this customer, this order, a database row with its key), from Value Objects, whose equality is their fields. Two Money(5.00, USD) objects are different objects, so a == b is false, but they are the same money: a.equals(b) compares currency and amount, and hashCode() is computed from the same fields so the two agree (Demo 4: equal, not identical).

A Value Object should be immutable. If two variables share one object and one of them could change it, the other would see the change: an aliasing bug. The book's example is two people hired on the same day sharing one date object; moving one person's date would move the other's. Money has no setter. a = a.add(x) makes a new object and moves only a's arrow; c, which shared the old object, still reads $5.00. The demo shows the bug a setter would cause as one red step, then takes it back. In C# a struct gives value semantics; in a database a Value Object is usually stored as columns of its owner (an Embedded Value), not as a row of its own. Copying, sharing and mutating references in general is on Pass by Value vs Pass by Reference.

Currency-aware arithmetic

add, subtract and compareTo first call assertSameCurrencyAs. The simplest rule, and the most common, is that mixing currencies is an error: $5.00 + $3.00 is a new Money of $8.00, while $5.00 + €5.00 throws and creates nothing (Demo 3: currency mismatch). Richer designs exist. Ward Cunningham's money bag holds amounts in several currencies at once and can be valued in one of them later. Conversion is best kept in a converter object, because some conversions are not a plain multiplication (the old euro conversions had their own rounding rules).

Multiplying needs a rounding mode

Multiplying money by a number, such as a tax rate, rarely gives whole cents, so multiply takes a rounding mode. The book's class defaults to HALF_EVEN, which rounds an exact tie to the even neighbour (banker's rounding), so ties don't all push the same way.

mode3.5¢1.5¢10.5¢16.4175¢
HALF_EVEN4¢2¢10¢16¢
HALF_UP4¢2¢11¢16¢
DOWN3¢1¢10¢16¢
UP4¢2¢11¢17¢

Where you round matters as much as how. Three lines of $0.70 at 5% have 3.5¢ of tax each. Rounded per line that is 4¢ + 4¢ + 4¢ = 12¢; rounded once on the $2.10 total it is 10.5¢ → 10¢ with HALF_EVEN, or 11¢ with HALF_UP (Demo 7: per line vs total). Neither answer is wrong. The business rule has to say which one, and the code has to round in that place.

Two columns for the invoice of three lines of 70 cents at 5 percent. Per line: 3.5 cents rounds to 4 cents three times, total 12 cents. On the total: 2.10 dollars times 5 percent is 10.5 cents, which HALF_EVEN rounds to 10 cents. The two differ by 2 cents.
Rounding each line and rounding the sum are two different answers, here 12¢ against 10¢.

The rate itself must be exact. new BigDecimal("0.05") is 0.05. new BigDecimal(0.05), which the book's convenience multiply(double) uses, is the double nearest to 0.05: 0.05000000000000000277…. On the $2.10 total that turns the tie 10.5¢ into 10.50000000000000058…¢, and HALF_EVEN now gives 11¢ instead of 10¢ (Demo 8: double rate). The float came back in through the rate.

Foemmel's conundrum and allocate

Matt Foemmel's puzzle: a rule says a sum must go to two accounts, 70% to one and 30% to the other, and the sum is 5 cents. The exact shares are 3.5¢ and 1.5¢. Round both to nearest and you pay out 4¢ + 2¢ = 6¢, a cent that never existed; round both down and you pay 3¢ + 1¢ = 4¢, a cent gone. Every single rule applied to both shares is off by one (Demo 5: Foemmel's 5¢).

Before: 5 cents split 70/30 is 3.5 and 1.5 cents; rounding both to nearest gives 4 plus 2, 6 cents, one cent made; rounding both down gives 3 plus 1, 4 cents, one cent lost. After: allocate 7 to 3 gives the floors 3 and 1, the remainder of 1 cent goes to the first share, 4 plus 1 is 5 cents.
Rounding each share can't add up; allocate rounds every share down and hands the leftover cents out one by one.

The usual fixes are to ignore the stray penny (accountants dislike that), to let the last share take whatever is left (it can collect many pennies), or to make the caller choose a rounding mode per share (awkward beyond two shares). Fowler's choice is an allocator on Money. allocate(long[] ratios) gives every share amount × ratio / total with integer division, which rounds down, then works out the remainder, the cents the rounding dropped, and gives them out one at a time from the first share on. For 5¢ at 7 : 3 the floors are 3¢ and 1¢, the remainder is 1, and the result is 4¢ + 1¢ = 5¢. The book's own test uses the ratios 3 : 7 and gets 2¢ + 3¢. No share is more than one cent from its exact value, and the parts always add up to the whole.

allocate(int n) splits evenly: every part gets amount / n and the first amount % n parts get one cent more. $100 three ways is $33.34 + $33.33 + $33.33, where rounding $33.333… three times would give $99.99 (Demo 6: $100 three ways). The price is that someone gets the extra cent. The page gives it to the first shares, as the book's code does; a real system may need a rule for that (largest share, a rotating order, the customer's favour).

Allocate or multiply? Ask what the number is for

you wantusewhy
a charge proportional to an amount: tax, fee, discount, interestmultiply(rate, mode)one result; the rounding mode is part of the business rule
a sum divided into parts that must add up: a bill split, a payment over instalments, revenue shared between accountsallocate(ratios) or allocate(n)no cent can be lost or made; only the place of the leftover cents is a choice

What the page leaves out

Real money libraries (Java's JSR 354 / Moneta, Joda-Money, Python's decimal with a currency) also format and parse amounts per locale, convert between currencies with dated rates and their own rounding rules, keep money bags, and store Money either with a currency column per row or with the currency on the account. The page only uses non-negative amounts: with Java's integer division, the book's allocate would drop cents on a negative sum (−5¢ at 7 : 3 gives −3¢ + −1¢), so real code allocates the absolute value and puts the sign back. It also skips the other RoundingModes (FLOOR, CEILING, HALF_DOWN), fractional-cent money types and rules for which share gets the leftover cents.

On the mind map

This page on the Software Design mind map · 28 pages in 7 branches · open it to see the whole map and test yourself in recall mode.