← Back to Blog

Exact Money: Why NeoDonkey Stores Cents as BigInt

August 21, 2026 · 4 min read

Every ERP system handles money. Almost all of them get it wrong.

The Problem with Floats

JavaScript's Number type is IEEE 754 double-precision floating point. It cannot represent most decimal fractions exactly:

0.1 + 0.2 === 0.3  // false
0.1 + 0.2          // 0.30000000000000004

This is not a JavaScript bug. It is a property of binary floating point. And it means that if you store €0.10 as a float, you do not have €0.10. You have an approximation that happens to round to €0.10 when you print it.

Most ERP systems work around this by using "decimal" libraries that store numbers as strings or arrays of digits. This is better, but still has a problem: the string "10.00" is not the same type as the string "10", and comparing them requires parsing. Worse, some systems store money as number in JSON, which means the float problem reappears at every API boundary.

NeoDonkey's Approach

In NeoDonkey, money is a token: "1000.00 EUR". This is not a string that happens to look like money. It is a structured value with three parts:

  1. Amount: A BigInt of minor units (cents). €10.00 is 1000n.
  2. Precision: How many decimal places the currency uses. EUR has 2.
  3. Currency: The ISO 4217 code. "EUR", "USD", etc.

The token "1000.00 EUR" parses to { amount: 1000n, precision: 2, currency: "EUR" }. All arithmetic happens on the BigInt amount. There is no float anywhere in the ledger.

Why This Matters for an ERP

An ERP is not a calculator. It is a system of record. The difference between 1000n and 1000.0000000000001 is not a rounding error — it is a bug in the truth layer.

When NeoDonkey posts a journal entry, the invariant checker verifies:

debit-amount == sum of all debit postings
credit-amount == sum of all credit postings
debit-amount == credit-amount

These comparisons use exact BigInt equality. There is no epsilon. No rounding tolerance. Either the books balance exactly, or the post is rejected.

Where the Code Lives

The Test That Caught It

Our demo fixture used to write net-amount: 2160 for 240 units at €9.00. The parser now rejects this — correctly. The amount must be 2160.00 EUR, because 2160 without a currency is not money, and 2160.0 without a trailing zero has the wrong precision for EUR.

This is not pedantry. It is the difference between a system that rounds and a system that records.


NeoDonkey is an open-source ERP that stores your company as a git repository. Every booking is a signed commit. Try the demo.