Numeric Types: smallint, integer, bigint, numeric, real, double precision
corebeginnersmallint, integer and bigint are exact whole numbers of increasing range. numeric (aka decimal) is exact with arbitrary precision, ideal for money. real and double precision are approximate floating-point — fast, but not exact, and wrong for money.
Think of it as
The real dividing line is not "small vs large" but "exact vs approximate." integer/bigint/numeric always represent the value written exactly. real/double precision are IEEE 754 floating-point, which cannot represent most decimal fractions exactly — 0.1 stored as a double is not really 0.1. Choosing between them is really choosing whether occasional tiny representation error is acceptable.
What we're doing: Show the exact-vs-approximate difference directly by comparing 0.1 + 0.2 under numeric and double precision.
- 1
- double precision arithmetic — IEEE 754 cannot represent 0.1 or 0.2 exactly in binary.
- 2
- numeric arithmetic — stores and computes the exact decimal value, no representation error.
float_sum | numeric_sum | float_equals_exact | numeric_equals_exact
--------------------+-------------+---------------------+----------------------
0.30000000000000004 | 0.30 | f | tWhy this works: double precision stores numbers in binary floating-point, and 0.1 and 0.2 have no exact finite binary representation (the same way 1/3 has no exact finite decimal representation) — the stored values are already tiny approximations before any arithmetic even happens, so their sum is a slightly-off approximation too. numeric stores the exact decimal digits directly rather than converting to binary, so 0.1 + 0.2 in numeric arithmetic is genuinely, exactly 0.3, with no representation error at any step.
Using double precision (or real) to store monetary amounts
Wrong
Better
What you see: Account balances drift by fractions of a cent after many transactions, or a balance check like WHERE balance = 100.00 fails to match a value that displays as exactly "100.00," because the stored floating-point value is actually 99.99999999999997 or similar.
Why: Every floating-point arithmetic operation can introduce a small representation error, and those errors compound across repeated additions/subtractions in a way that is unpredictable and, over enough transactions, becomes financially significant — this is a property of IEEE 754 itself, not something PostgreSQL can fix. numeric was specifically designed to avoid this entire class of problem by never converting to binary floating-point in the first place, which is why it is the standard, non-negotiable choice for any exact monetary or accounting value.
- Exact: integer / bigint / numeric
- represents the written value precisely
- numeric arithmetic never introduces rounding error
- the correct family for money, counts, IDs
- Approximate: real / double precision
- IEEE 754 floating-point — cannot represent most decimals exactly
- 0.1 + 0.2 = 0.30000000000000004 in double precision
- fine for scientific/measurement data, wrong for money
The numeric type family at a glance
Together
Remember: integer/bigint/numeric are exact; real/double precision are approximate IEEE 754 floating-point that cannot represent most decimals exactly — never use float types for money.
See also: date time types · money vs numeric

