Numeric Types
PostgreSQL offers three broad families of numeric type: fixed-size integers for whole numbers, exact arbitrary-precision types for money and other values where rounding errors are unacceptable, and floating-point types for approximate values like scientific measurements. Picking the right family matters as much for correctness as for storage efficiency.
Integer types
The three integer types differ only in their storage size and the range of values they can hold. Pick the smallest one that comfortably covers your expected range — going bigger than you need wastes storage across millions of rows for no benefit.
Type | Storage | Range |
|---|---|---|
| 2 bytes | -32,768 to 32,767 |
| 4 bytes | -2,147,483,648 to 2,147,483,647 |
| 8 bytes | -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 |
Declaring integer columns
CREATE TABLE products (
product_id INTEGER PRIMARY KEY,
quantity SMALLINT NOT NULL DEFAULT 0,
total_views BIGINT NOT NULL DEFAULT 0
);INTEGER is the practical default for most whole-number columns — IDs, counts, quantities. Reach for BIGINT when a value could realistically exceed about two billion (a high-traffic view counter, a globally-shared sequence), and SMALLINT only when you know the range is genuinely tiny and storage is at a premium.
Exact numeric types: NUMERIC and DECIMAL
NUMERIC (and its exact synonym DECIMAL) stores numbers with user-specified precision and scale — NUMERIC(precision, scale) — and every digit you write is stored and retrieved exactly. There is no rounding introduced by the storage format itself.
Exact precision for money
CREATE TABLE invoices (
invoice_id INTEGER PRIMARY KEY,
amount_due NUMERIC(10, 2) NOT NULL -- up to 10 digits total, 2 after the decimal
);
INSERT INTO invoices VALUES (1, 199.99);
INSERT INTO invoices VALUES (2, 0.10);
SELECT amount_due + amount_due FROM invoices WHERE invoice_id = 2;
-- 0.20, exactly — no floating-point surprisesApproximate numeric types: REAL and DOUBLE PRECISION
REAL (4 bytes, ~6 decimal digits of precision) and DOUBLE PRECISION (8 bytes, ~15 decimal digits of precision) are IEEE 754 floating-point types. They're fast and compact, but approximate — perfectly fine for scientific measurements, sensor readings, or graphics coordinates where tiny rounding differences don't matter, but wrong for anything that needs exact decimal arithmetic.
Floating-point for scientific data
CREATE TABLE sensor_readings (
reading_id BIGSERIAL PRIMARY KEY,
temperature_c DOUBLE PRECISION NOT NULL,
recorded_at TIMESTAMPTZ NOT NULL DEFAULT now()
);Auto-incrementing integers: a quick preview
You'll frequently see SERIAL and BIGSERIAL used for auto-incrementing primary key columns — they're shorthand built on top of INTEGER/BIGINT plus a sequence. They get a full page of their own (SERIAL & Identity Columns) alongside the modern GENERATED ... AS IDENTITY syntax that PostgreSQL now recommends instead.
SMALLINT/INTEGER/BIGINTare exact whole-number types that differ only in storage size and range.NUMERIC(precision, scale)stores exact decimal values — the only correct choice for money.REAL/DOUBLE PRECISIONare fast but approximate floating-point types — never use them for currency.SERIAL/BIGSERIALare auto-incrementing shorthand covered in depth on their own page.