Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

Low-Level Design (Object-Oriented Design)  Module 5 – Modelling lifecycles, time, money and rules

Money arithmetic: paise, rounding and splits

Keep money exact in integer paise or decimals, name each rounding rule and where it applies, and split totals so that no paisa is lost or invented.

  • Intermediate
  • 30 minutes
  • Examples run with Python 3.14.8, Pyodide 314.0.7, Node.js 24.21.0 and quickjs 0.32.0
  • By MySmartCoPilot
Free intro, Pro: 7 of 30 minutes free
Free: Why a float loses paise, and what keeps them. With a pass: the other 9 sections and 3 interview answers, about 23 minutes.

What you will learn

  • Represent money as integer minor units or decimal values that carry a currency
  • Apply a named rounding mode at a named place, per line or per invoice
  • Split amounts by allocation so that the parts always add up to the total
  • Store tax rates and fees in configuration with the dates they apply from

Before you start

What you’ll learn · Before you start

What you will learn

  • Represent money as integer minor units or decimal values that carry a currency
  • Apply a named rounding mode at a named place, per line or per invoice
  • Split amounts by allocation so that the parts always add up to the total
  • Store tax rates and fees in configuration with the dates they apply from

Before you start

On this page

Money in code needs three decisions that binary floating-point numbers make silently, and wrongly. The first is the representation: an amount is a whole number of minor units, such as paise or cents, or a decimal value, and it always carries its currency. The second is rounding: when a result falls between two paise, a named rounding mode decides, at a named place, per line or per invoice. The third is splitting: when a total is divided, the paise left over are handed out by a fixed rule, so the parts add back up to the total. Tax rates and fees are inputs to these rules, kept as configuration with the dates they apply from.

Why a float loses paise, and what keeps them

A binary float stores fractions as sums of halves, quarters and eighths, so 0.1, 0.2 and most amounts of paise or cents are kept as the nearest binary value, not exactly. The errors are tiny, but they add up, and they surface where money is least forgiving: in totals that compare unequal and in splits that do not add up. A short script shows both, beside the integer and decimal versions that keep every paisa:

0.1 + 0.2, and 1,000 rupees split three ways Python · opening/thousand_three_ways.py
"""0.1 + 0.2 in binary floating point, then 1,000 rupees split three ways: as a float, as shares rounded to
whole paise one by one, and as paise allocated so that the shares add up to the bill."""

from decimal import Decimal


def rupees(paise):
    """Formats a whole number of paise without ever making a float."""
    return f"{paise // 100:,}.{paise % 100:02d}"


print("0.1 + 0.2 as floats:           ", 0.1 + 0.2)
print("10 + 20 paise as integers:     ", 10 + 20)
print("Decimal('0.1') + Decimal('0.2'):", Decimal("0.1") + Decimal("0.2"))
print()

bill = 100_000  # 1,000 rupees, in paise
print("1,000 rupees / 3 as a float:   ", 1000 / 3)
each = round(bill / 3)  # every share rounded to whole paise on its own
print(f"each share rounded to paise:    {rupees(each)} x 3 = {rupees(each * 3)}, one paisa short")
base, left = divmod(bill, 3)
shares = [base + 1 if i < left else base for i in range(3)]
print(f"allocated:                      {' + '.join(map(rupees, shares))} = {rupees(sum(shares))}")

Output

0.1 + 0.2 as floats:            0.30000000000000004
10 + 20 paise as integers:      30
Decimal('0.1') + Decimal('0.2'): 0.3

1,000 rupees / 3 as a float:    333.3333333333333
each share rounded to paise:    333.33 x 3 = 999.99, one paisa short
allocated:                      333.34 + 333.33 + 333.33 = 1,000.00

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 thousand_three_ways.py

The float sum printed 0.30000000000000004. The split shows a problem that remains even with exact numbers: 1,000 rupees do not divide into three equal amounts of whole paise, so rounding each share gives 333.33 three times, 999.99 in all, and a paisa disappears. Allocation fixes it by giving the leftover paisa to one share, by a rule anyone can predict.

Flow of an amount: parsed from text to paise or a decimal, computed exactly, rounded once by a rule, split, then formatted.price list, form or API"1,250.50" as text, with its currencyparse, never through a float125050 paise or Decimal("1250.50")add, multiply, apply ratesexact: nothing rounded yetround once, where the rule saysper line or per invoicehalf-up or half-evensplit a totalleftover paise handed out by a fixed ruleformat at the edge1,250.50 INRrates from configurationwith the day each applies from

Where each money decision lives

Text description of the diagram

Six steps run from top to bottom.

  1. An amount arrives from a price list, a form or an API as text, such as "1,250.50", together with its currency.
  2. It is parsed without passing through a float, into whole minor units such as 125050 paise in a 64-bit integer, or into a decimal such as Decimal("1250.50").
  3. Additions, multiplications and rates are computed exactly, with nothing rounded yet. Rates and fees come in from configuration, each with the day it applies from.
  4. The result is rounded once, where the business rule says (per line or per invoice), with a named mode such as half-up or half-even.
  5. When a total is split, the leftover paise are handed out by a fixed rule, so the parts add up to the total.
  6. Only at the edge is the amount formatted for people, such as "1,250.50 INR".

Each decision has its place on that path, and each tool has one job:

  • Integer minor units hold an amount as a whole count of paise or cents, so sums and comparisons are exact.
  • Decimal types, such as Python’s decimal and Java’s BigDecimal, store base-10 digits with no binary error and apply a rounding mode only when the code asks for one.
  • Rounding modes settle ties such as half a paisa: half-up rounds them away from zero, half-even to the even neighbour.
  • The place of rounding, per line or once per invoice, is a business rule that can move a total by paise.
  • Allocation hands out the leftover paise of a split one at a time in a fixed order, so the parts add up.
  • Currency checks refuse to add amounts in different currencies; converting is a separate, explicit step.
  • Rates as configuration keep tax percentages and fees as data with the day they apply from.
  • Safe integers matter in JavaScript, whose numbers are doubles: whole paise stay exact only up to 2⁵³ − 1.

The same invoice three ways: floats, paise and decimals Pro: opens with a Pro, Premium or Ultimate pass

In this part:

  • Rounding is a business rule: the mode, and where it applies
  • Splitting without losing a paisa
  • Currency belongs to the amount
  • JavaScript: safe integers and BigInt
  • Rates as configuration, with the day they apply from
  • Practise: an invoice with dated rates and one rounding
    • An invoice total with dated tax rates and one rounding
  • Check yourself
  • Interview questions
    • Why should a payment system not keep amounts in a binary floating-point type such as double?
    • A bill of 1,000 rupees is shared equally by three friends, and another of 100 rupees by the ratio 3 : 2 : 2. Design a split function whose parts always add up to the bill, and say who receives each leftover paisa and why.
    • Your billing service shows 18.00 rupees of tax on an invoice line of 100.05 rupees, but a customer's spreadsheet shows 18.01. The line carries 9 % CGST and 9 % SGST. Explain the difference, and decide where your system should round and how it should show the result.
    • A Node.js service keeps balances as JavaScript numbers in rupees, such as 1234.5. Some totals end in long strings of digits, and a large merchant's monthly settlement is a few paise off. What is wrong, what do you change in the code and in the API, and how do you migrate safely?
  • Key takeaways

7 runnable examples (JavaScript, Python, allocate.mjs, allocate.py) · 1 exercise with sample tests · 3 interview answers · about 21 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

Read a complete sample first: Low-level design: parking lot (the model)

References

Related tools

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.