All blogs

Engineering

Why we store stock quantities as integers

Add 0.1 to 0.2 in floating point and you don't get 0.3. That tiny lie is how inventory systems drift by fractions of a unit until nobody trusts the report. Our fix is old and boring.

DM

Daniel Mensah

Engineering · · 6 min read

Ask a computer for 0.1 + 0.2 and it answers 0.30000000000000004. Every language, every database float, same answer. Binary machines can't represent most decimal fractions exactly, so they store the nearest value they can. For a graphics engine that's fine. For a stock ledger it's corrosive, because a ledger's one job is that the entries sum to the balance. Exactly.

Pour 0.1 of a bottle three hundred times in floating point and the leftovers don't cancel. They accumulate into a phantom fraction of a bottle that no one poured and no count will ever explain. The report starts saying things like 2.9999999999999996, and the moment a stock system shows a number like that, trust in every other number on the page dies with it.

Base units, not decimals

Our fix is older than the problem: don't store fractions at all. Every product in Stocked declares a base unit: the smallest amount the business ever moves. Millilitres for the bar, grams for the kitchen, single units for the shelf. Stock is then stored as an integer count of that base unit, and integer arithmetic is exact: the ledger sums to the balance to the last millilitre, every time, forever.

A bottle of gin isn't 1.0 of anything. It's 700 millilitres. A 35 ml pour subtracts 35. Receiving a case of six adds 4,200. The 'bottle' and the 'case' still exist, but as conversions defined on the product, used at the edges (on labels, in reports, at the till) and never in the arithmetic.

Where the design pays off

The payoff shows up everywhere quantities get awkward.

  • Pours and portions: the till sells a glass, the ledger moves millilitres, and three hundred pours later the maths is still exact.
  • Recipes: a cocktail draws 50 ml of one integer and 25 of another, with no rounding decisions and no drift.
  • Partial receiving: 3 bottles of a 6-bottle case is 2,100 ml, not 0.5 cases and a debate.
  • Audits: every balance is provably the sum of its history, so a variance is always real, never an artefact of the arithmetic.

The boring rule

The same discipline applies to money, which is why prices live as integer kobo and cents rather than floats of naira and dollars. The rule generalises: store quantities in the smallest unit you'll ever need, as integers, and let formatting happen at the last moment before a human sees the number. It's the least glamorous decision in the codebase, and it's load-bearing for every report we ship. Boring is what correct looks like at three in the morning.

Share this post

Stocked is the system these notes are written from. Start free or read more from the blog.

Read next

All posts