PostgreSQL – Storing Floating Point Values for Various Units

database-designfloating pointpostgresql

I have a table like the following:

order_item
------------
serial      | id (PK)
integer     | order_id
????        | quantity
integer     | unit_of_measure_id (PK) --> ml, l, kg, mg, mm, m, etc.

I'm doing an application where I need to store the amount of material used in chemical analysis. Sometimes materials are used in such a small amount as microliters, or sometimes more, like one or two liters.

Is it the numeric data type good enough?

I have read that it is a good idea to convert the values to the smallest unit of measure. So if I have 10 liters, I have to pass to 10000000 microliters. But what if you need to use a unit of measurement even lower in the future? like picoliters? Inevitably, the application will have to use floating point numbers I think.

Best Answer

If you need precise storage for numbers with yet unknown precision, the data type numeric is the way to go. Like @dezso already commented, it stores what you pass exactly without rounding, up to an insane amount of digits. Per documentation:

up to 131072 digits before the decimal point; up to 16383 digits after the decimal point

Be aware, however, that even numeric cannot store the result of calculations like 1::numeric/3 exactly, since that is impossible on principal. Postgres seems to truncate the result at a scale of 20 unless instructed otherwise, while JDBC only displays a scale of 12:
SQL Fiddle.

You pay a price for extreme exactness. Per documentation:

However, arithmetic on numeric values is very slow compared to the integer types, or to the floating-point types described in the next section.

Won't matter much as long as you don't do heavy computations with your numbers.

As for units, I would not use multiple competing units for the same unit of measurement. Go with just one. So just m for meter, not mm or km etc.

I would stick to SI units if that's at all practical for your use case.

You already mentioned the alternative for better performance: combine integer (or bigint?) numbers with the smallest units of measure.