Money in PostgreSQL and Java: NUMERIC and BigDecimal
Float and double are binary: 0.1 has no exact representation,
the same way 1/3 never terminates in decimal. Summing cents in a double works in
the test and gets the customer's statement wrong.
System.out.println(0.1 + 0.2); // 0.30000000000000004
In the database: NUMERIC with explicit scale
CREATE TABLE orders (
id BIGSERIAL PRIMARY KEY,
total NUMERIC(12, 2) NOT NULL
);
NUMERIC(12,2) stores the exact value: 12 digits total, 2 after
the decimal point. Never REAL or DOUBLE PRECISION for
money. If the domain has rates with more decimals (FX, daily interest), raise the
scale of that specific field, like NUMERIC(18,6), instead of loosening
everything.
In Java: BigDecimal, and watch out for equals
@Column(nullable = false, precision = 12, scale = 2)
private BigDecimal total;
Three classic traps:
new BigDecimal(0.1)imports the double's error. Usenew BigDecimal("0.1")orBigDecimal.valueOf(0.1).equalscompares scale:new BigDecimal("1.0")is not equal tonew BigDecimal("1.00"). To compare value, usecompareTo.- Division needs an explicit
RoundingMode:total.divide(n, 2, RoundingMode.HALF_EVEN). HALF_EVEN (banker's rounding) avoids accumulated bias over large sums.
In Python the equivalent is decimal.Decimal (Pydantic maps
NUMERIC to it); in TypeScript there's no native decimal type, so either the
value travels as a string, or in integer cents, or you accept the risk of
number.