---
title: "Rekenen met hele centen, en de korting die verdween"
description: "We gaven vijftig cent korting en het grootboek legde er niets van vast. Floating point was niet eens de boosdoener. Een veldgids voor rekenen met geld in hele getallen: begrenzen, verdelen, en een grootboek van stuksprijzen precies op het totaal laten uitkomen."
url: "https://bookatu.com/nl-nl/blog/money-math-in-integer-cents"
language: "nl"
section: "Blog"
category: "Techniek"
author: "Het technische team van Bookatu"
published: "2026-07-13"
updated: "2026-07-13"
tags: ["geld in centen", "floating point en valuta", "korting verdelen over regels", "afrondfouten", "betalingen bouwen"]
---

# Rekenen met hele centen, en de korting die verdween

We gaven vijftig cent korting en het grootboek legde er niets van vast. Floating point was niet eens de boosdoener. Een veldgids voor rekenen met geld in hele getallen: begrenzen, verdelen, en een grootboek van stuksprijzen precies op het totaal laten uitkomen.

> **In het kort**
>
> - Sla geld op als hele centen en reken er ook zo mee. Floats zijn de beroemde bug, je afrondbeleid is de echte.
> - Een korting die je over regels verdeelt moet kloppend worden gemaakt: rond het aandeel van elke regel af en duw de overgebleven centen daarna naar regels met ruimte, tot de som exact is.
> - Slaat een grootboek één stuksprijs per regel op, dan kun je een kortingstotaal dat niet netjes deelt niet weergeven. Splits de regel in twee prijzen die één cent uit elkaar liggen.
> - Begrens alles aan de randen: kortingen tot aan het subtotaal, aantallen tot aan de voorraad, totalen tot nul.

Elke ontwikkelaar leert vroeg dat 0.1 plus 0.2 geen 0.3 is, dus slaan we geld allemaal op als hele centen en voelen we ons veilig. Dan bouw je iets echts, zoals een procentuele korting over een winkelmandje, en ontdek je dat hele getallen alleen het eerste probleem oplossen. Het tweede probleem is dat delen niets om je grootboek geeft.

## De vijftig cent die zoekraakte

Dit is de opzet die ons beet, teruggebracht tot het bot. Een mandje bevat 100 stuks van een artikel van een dollar. De klant krijgt vijftig cent van de bestelling af. Het subtotaal is 10.000 cent, de korting is 50 cent, en het totaal na korting is 9.950 cent. Tot zover niets anders dan hele getallen.

Leg die verkoop nu vast in een grootboek dat een stuksprijs per regel opslaat, zoals boekhoudsystemen dat graag doen. De stuksprijs na korting is 9.950 gedeeld door 100, oftewel 99,5 cent. Dat is geen heel getal, dus wordt het afgerond naar 100. Het grootboek zegt nu 100 stuks van 100 cent: een verkoop tegen de volle prijs. De korting kromp niet. Hij verdween, terwijl het scherm de klant 9.950 beloofde en de boeken 10.000 zeiden.

```js
// The trap: one rounded unit price cannot represent every total.
const subtotal = 100 * 100;              // 10000c
const discounted = subtotal - 50;        // 9950c
const unit = Math.round(discounted / 100); // 100c ... the discount is gone
console.log(unit * 100);                 // 10000c, not 9950c

// The fix: split the line into two prices one cent apart.
const qty = 100;
const base = Math.floor(discounted / qty);   // 99c
const rem = discounted - base * qty;         // 50 units get one extra cent
// rem units at (base + 1), the rest at base:
// 50 * 100 + 50 * 99 === 9950  ✓ exact, and every price is an integer
```

Die splitsing lijkt pietluttig tot je ziet wat hij oplevert. Vijftig stuks van 100 cent plus vijftig stuks van 99 cent is precies 9.950. Elke regel slaat nog steeds een eerlijke hele stuksprijs op, de aantallen kloppen nog steeds, en de som van het grootboek is tot op de cent gelijk aan het bedrag op de bon. Niets daarvan kan met één afgeronde prijs.

## Verdelen vraagt om een sluitronde

Dezelfde kwaal duikt een niveau hoger op als een korting op de hele bestelling zich over meerdere regels verspreidt. Geef elke regel zijn evenredige aandeel en rond af, en de aandelen tellen op tot bijna de korting. Soms een cent te veel, soms een cent te weinig, af en toe precies goed, en dat is de slechtste uitkomst van allemaal, want het verbergt de bug voor wie er losjes op test.

```js
// Prorate by share, then push the rounding leftover onto lines with room.
let allocated = 0;
for (const line of lines) {
  line.discount = Math.min(
    line.subtotal,
    Math.round((discount * line.subtotal) / orderSubtotal),
  );
  allocated += line.discount;
}
let remainder = discount - allocated; // may be negative
for (const line of lines) {
  if (remainder === 0) break;
  const room = remainder > 0 ? line.subtotal - line.discount : line.discount;
  const move = Math.min(Math.abs(remainder), room) * Math.sign(remainder);
  line.discount += move;
  remainder -= move;
}
// invariant: sum(line.discount) === discount, and no line goes negative
```

Twee details in die lus hebben zich op de harde manier bewezen. De rest wordt verdeeld over elke regel met ruimte en niet alleen over de laatste, want de laatste regel kan een artikel van zes cent zijn dat geen drie overgebleven centen kan opnemen. En elk aandeel wordt afgetopt op het subtotaal van de eigen regel, want een korting die groter is dan de regel betekent een negatieve stuksprijs, en met negatieve stuksprijzen begint een grootboek de andere kant op te liegen.

![Een kassascherm met een subtotaal, tien procent korting en het exacte totaal na korting](https://bookatu.com/blog/shots/pos-discount-light.png)

*De saaie bon is de hele bedoeling: subtotaal, korting, en een totaal dat het grootboek tot op de cent reproduceert.*

## Begrens aan elke rand

- Kortingen begrens je tussen nul en het subtotaal. Honderdtien procent korting is een typefout, geen terugbetaling.
- Lees door gebruikers ingevoerde bedragen defensief uit: leeg en niet-numeriek betekenen nul, nooit NaN midden in een som.
- Aantallen begrens je tot wat er is. Code die met geld rekent erft elke voorraadbug die erboven zit.
- Test de totalen die niet netjes delen. Drie artikelen van tien dollar met een dollar korting is een betere test dan welk rond getal ook.

Niets hiervan is glamoureus, en precies daarom glipt het door de review. Het rekenwerk ziet er correct uit omdat het correct is, tot het moment dat een deling met hele getallen stilletjes een rest weggooit waar iemand recht op had. Schrijf de regel op, de som van de delen is gelijk aan het geheel, en zet er een test voor de deur.

> Hele getallen houden geld eerlijk. De sluitronde houdt hele getallen eerlijk.
