Step 2: Represent money exactly
Never use double for money. Binary floating point cannot store most decimals exactly, so errors creep in. Run this and watch 0.1 + 0.2 fail to equal 0.3:
ExampleJava
public class Main {
public static void main(String[] args) {
// Floating point cannot store most decimals exactly
double a = 0.1 + 0.2;
System.out.println(a);
System.out.println(a == 0.3);
double total = 0;
for (int i = 0; i < 10; i++) total += 0.1;
System.out.println(total);
System.out.println(total == 1.0);
// Whole cents in a long are exact
long cents = 0;
for (int i = 0; i < 10; i++) cents += 10; // ten times 10 cents
System.out.println(cents + " cents");
System.out.println(cents == 100);
}
}Output
0.30000000000000004 false 0.9999999999999999 false 100 cents true
The fix is to store whole cents in a long: integers are exact. Wrap them in a small immutable type so the rest of the program cannot mix up cents and dollars, and so printing is in one place. A Java record gives you equality by value, a constructor and accessors for free.
ExampleJava
public class Main {
record Money(long cents) {
static Money of(long dollars, int cents) { return new Money(dollars * 100 + cents); }
static final Money ZERO = new Money(0);
Money plus(Money other) { return new Money(cents + other.cents); }
Money minus(Money other) { return new Money(cents - other.cents); }
boolean isNegative() { return cents < 0; }
boolean lessThan(Money other) { return cents < other.cents; }
@Override
public String toString() {
String sign = cents < 0 ? "-" : "";
long abs = Math.abs(cents);
return String.format("%s$%d.%02d", sign, abs / 100, abs % 100);
}
}
public static void main(String[] args) {
Money price = Money.of(19, 99);
Money tip = Money.of(0, 5);
System.out.println(price);
System.out.println(price.plus(tip));
System.out.println(Money.ZERO.minus(tip));
System.out.println(price.equals(Money.of(19, 99))); // records compare by value
Money sum = Money.ZERO;
for (int i = 0; i < 10; i++) sum = sum.plus(Money.of(0, 10));
System.out.println(sum);
}
}Output
$19.99 $20.04 -$0.05 true $1.00
Because Money is immutable, plus and minus return new values, so nobody can change a price by accident.
Quick check
Why store money as whole cents in a long?
Finish the quick check (and any code exercise) to complete this lesson, or mark it done yourself.
Tip: use the left and right arrow keys to move between lessons.