The Machines · Money & finance

money

Exact Money — machines/money.shoddy

the money machine's icon

Summary

money holds an amount of money exactly, so the pennies always add up. The trick is simple: instead of storing $19.99 as the fraction 19.99, it stores it as the whole number 1999 — a count of cents. A computer can hold whole numbers perfectly. It cannot hold fractions of a dollar exactly, and the tiny errors that creep in from those fractions are exactly what makes a total come out a penny wrong. By keeping everything in whole cents, money sidesteps that entirely. You build amounts from dollars or from a typed-in string, add and subtract and compare them, multiply by a rate (tax, interest) with the rounding made explicit, split a sum into even shares without losing or inventing a penny, and format the result back into a familiar $12.34. Everything is pure — each word hands you back a new Money and never changes the one you gave it.

A Brief History of the Whole Cent

Ledgers never had a rounding problem. A Victorian clerk kept accounts in pounds, shillings and pence because money is a count — of coins, ultimately — and counts, added down a column, always balance to the penny. Computing broke that guarantee by an accident of base. Binary floating point — the base-two number format computers calculate in — cannot hold one tenth exactly, for the same reason decimal notation cannot write a third. 0.1 in a double is a very close lie, and a few million additions of a very close lie is a total that is visibly wrong. Business computing knew this from the start: COBOL gave money its own decimal arithmetic in 1959 rather than trust the engineering formats. And the folklore of fractions of a cent quietly skimmed into someone's account is as old as payroll programs. The remedy has never changed. Keep money as an integer count of its smallest unit, and make every rounding a decision somebody took rather than an accident the hardware supplied. That is this machine — the clerk's column of pennies, restored.

Why It's Useful

Here is the problem this machine exists to solve. A computer stores ordinary decimal numbers in floating point — binary fractions — and binary simply cannot write down most decimal amounts exactly, in the same way you can't write one-third as a finite decimal (0.3333… never ends). The value $0.10 has no exact binary form; what the computer actually stores is a number a hair's breadth away from a tenth. Each such amount is off by a sliver far too small to see, and on its own it's harmless. But arithmetic piles those slivers up. Add $0.10 to itself again and again, or run a few thousand prices through a till, and the slivers accumulate until the total lands a whole cent — sometimes more — off from what a person adding the same figures on paper would get. The classic demonstration: on most computers 0.1 + 0.2 does not equal 0.3. It comes out as 0.30000000000000004. For a physics simulation, nobody cares. For money, a total that's a penny wrong is a bug report, an unbalanced ledger, an audit that doesn't reconcile.

The cure is to stop storing fractions at all. A dollar is a hundred cents, and a cent is a whole thing — you never need to represent half a cent to a customer. So money keeps the amount as a whole number of cents and does all its sums on those whole numbers. Whole numbers add, subtract, and compare with no drift whatsoever, because there are no fractions left to be inexact about. Rounding still has to happen the moment you multiply by a rate (8.75% tax on $19.99 genuinely lands between two cents). But here the rounding is done once, on purpose, to the nearest cent, where you can see it — not scattered invisibly through every operation. That's the whole idea: exact where exactness is free, and honest, deliberate rounding only where the arithmetic truly forces a choice. Reach for money any time real money is involved — prices, totals, invoices, splitting a bill. And reach for it instead of, never alongside, plain decimal dollars.

User's Guide

You make a Money from a number of dollars with Dollars, or from a string a person typed with MoneyVal. From there you add, subtract, sum a whole list, compare, multiply by a rate, split into shares, and format for display. Because every word is pure, you keep the result in a variable rather than expecting the original to change.

Include "money.shoddy"

Def Main()
    Let price = Dollars(19.99)            ' 1999 cents, held exactly
    Let tax   = MoneyMul(price, 0.0875)   ' 8.75% tax, rounded to the nearest cent
    Let total = MoneyAdd(price, tax)

    Print(MoneyFmt(price))                ' $19.99
    Print(MoneyFmt(tax))                  ' $1.75
    Print(MoneyFmt(total))                ' $21.74

    ' Split a bill three ways with no penny lost:
    Let shares = MoneySplit(Dollars(10.00), 3)
    Each(shares, Fn(s) => Print(MoneyFmt(s)))   ' $3.34, $3.33, $3.33

    Print(MoneyLt(tax, price))            ' True
    Print(MoneyFmt(MoneySum(shares)))     ' $10.00  (the parts add back up)

A few things worth remembering:

Word Reference

Every word, plus the Money type

The type

WordDescription
MoneyAn amount of money, held as a whole number of cents in its Cents field. That's the entire representation — no fractions, no hidden decimal. You rarely build one by hand; use Dollars or MoneyVal.

Making money

WordDescription
Dollars(d)Turns a number of dollars into a Money, rounding to the nearest cent as it converts — Dollars(19.99) is 1999 cents. The usual way to start.
MoneyVal(s)Turns a string a person typed (like "19.99") into a Money, by reading it as a number of dollars and then converting exactly as Dollars does.

Arithmetic

WordDescription
MoneyAdd(a, b)Adds two amounts. Exact — whole cents plus whole cents, no rounding, no drift.
MoneySub(a, b)Subtracts b from a. Exact, and may go negative (a refund or a balance owed).
MoneyMul(a, f)Multiplies an amount by a plain number f — a tax rate, an interest rate, a quantity — and rounds the result to the nearest cent. This is the one word that rounds, and it does so explicitly.
MoneySum(xs)Adds up a whole list of Money values into one total. Exact. An empty list sums to zero.

Comparing

WordDescription
MoneyLt(a, b)True or False: is a less than b? (Since amounts are whole cents, equality is just plain = on the values themselves.)

Splitting and formatting

WordDescription
MoneySplit(m, n)Divides m into n shares as evenly as whole cents allow, handing the leftover pennies to the earliest shares one apiece. The parts always sum back to exactly m — no penny lost, none invented.
MoneyFmt(m)Formats an amount as a familiar dollar string, like $12.34 — always two decimal places, and thousands grouped with commas: $1,234,567.89. Negative amounts get a leading minus, like -$4.50.

Sharing a program with clock? Include both bare — they get along. (They once collided over twin private Pad2 helpers. Both now lean on str's PadZero instead, each bringing it in through its own Include, and compiled include-once means one copy serves everybody.)

Who Uses It

UserHow
cuttleMoney is the type CutMoney carries; MoneyAdd, MoneySub and MoneyFmt back its arithmetic and display.
demographicsDollars and MoneyFmt print the predicted income as an exact dollar figure beside the raw model output.

The Machines It Uses

MachineWhy
strMoneyFmt is CommaGroup dollars and PadZero cents.