Data AnalyticsPython foundations for analysts

Python Decimal for money versus floating-point arithmetic

PK
Pankit Kumar
Sr. Data Scientist at Parexel (a Goldman Sachs–backed company) · 20 September 2026 · 3 min read
Technically reviewed by Ishaan Sharma
In this article (6 sections)

For monetary calculations that must reconcile to decimal source amounts, use Decimal constructed from validated text or integer minor units. A float remains useful for many statistical calculations, but its binary representation should not quietly define an invoice's rounding policy.

The important decision is more specific than choosing a Python type: define currency, scale, rounding mode and the point at which rounding occurs. Those decisions determine whether two independently produced reports agree.

Demonstrate the representation difference

Run this standard-library example in the Python reporting lab:

python
from decimal import Decimal, ROUND_HALF_UP, localcontext

assert 0.1 + 0.2 != 0.3
assert Decimal("0.1") + Decimal("0.2") == Decimal("0.3")
assert Decimal(0.1) != Decimal("0.1")
with localcontext() as context:
    context.prec = 28
    rounded = Decimal("2.675").quantize(Decimal("0.01"), rounding=ROUND_HALF_UP)
assert rounded == Decimal("2.68")
print("Decimal input and explicit rounding verified")

Constructing Decimal from a float preserves the float's represented value; it does not recover the original decimal text. The Decimal documentation describes construction, arithmetic contexts and quantize. The assertions above make the distinction observable without relying on how a notebook formats a number.

Reconcile an actual fixture

The synthetic lab contains eleven input records, including an identical replay and three rejected records. Its January report uses four valid paid orders: INR 19.00, 5.00, 10.00 and 13.50.

python
from decimal import Decimal
from pathlib import Path
from report import load_orders, paid_summary

with Path("raw_orders.csv").open(encoding="utf-8-sig", newline="") as handle:
    orders, rejected, replays, raw_count = load_orders(handle)
summary = paid_summary(orders, "2026-01")
assert summary["paid_amount_inr"] == "47.50"
assert sum((Decimal(v) for v in ["19.00", "5.00", "10.00", "13.50"]), Decimal("0.00")) == Decimal("47.50")
print(summary)

Decimal alone does not reject a duplicate order or a pending payment. The amount is correct because validation, identity rules, date boundaries and status filtering accompany the numeric type.

Choose where to round

Consider three synthetic fee components of INR 0.005 each. Rounding every component to two decimal places with half-up gives INR 0.03 in total. Summing first and rounding the total gives INR 0.02.

python
from decimal import Decimal, ROUND_HALF_UP

parts = [Decimal("0.005")] * 3
unit = Decimal("0.01")
per_line = sum((x.quantize(unit, rounding=ROUND_HALF_UP) for x in parts), Decimal("0.00"))
at_total = sum(parts, Decimal("0.00")).quantize(unit, rounding=ROUND_HALF_UP)
assert (per_line, at_total) == (Decimal("0.03"), Decimal("0.02"))
print(per_line, at_total)

Neither result becomes the business rule merely because Python produced it. Obtain the documented rounding policy and test it at the specified grain. This hypothetical example explains arithmetic; it is not a tax or invoicing rule.

When integer minor units help

If every accepted value is already an exact number of paise, store 19.00 as 1900 and sum integers. Conversion between major and minor units must still validate scale. Multiplying an arbitrary binary float by 100 and truncating is not a safe general conversion method.

Integer minor units also need currency metadata. One integer column cannot describe both a count and an amount, and currencies should not be summed together without an explicit conversion contract. Percentages, exchange conversions and allocations may require intermediate precision beyond the final display scale.

Review the handoff

The lab serializes monetary totals as decimal strings in JSON so a consumer does not immediately reintroduce binary floating-point interpretation. A receiving system should document how it parses those strings. If the output is CSV, include currency and unit definitions in its data dictionary.

Exercise: allocate INR 10.00 across three recipients. Produce minor-unit allocations whose sum remains exactly INR 10.00, specify who receives the remainder, and test that reordering recipients does not accidentally change an intended priority rule.

NeuraPath's Data Analytics with Generative AI course links Python calculations to report reconciliation. This exercise develops the habit of making numerical policy explicit before a polished dashboard hides the discrepancy.

Continue learning

This article is part of the Python foundations for analysts sequence. Use the neighbouring tasks when you need the prerequisite or the next application.

PK
Pankit Kumar
Lead Instructor, NeuraPath Academy

Pankit Kumar has 10 years in Data Science & AI, building and shipping production systems in regulated pharma and clinical environments. He is a freelance trainer at Boston Institute of Analytics, AnalytixLabs and Scaler, and has taught this material to thousands of working professionals.

This article is part of our Data Analytics with Generative AI programme — 3–4 months. The full analyst stack — Excel, SQL, Power BI and Python pipelines — then a generative-AI layer you can prove is right.

Explore Data Analytics with Generative AI
Counselling is free · no obligation

Not sure which programme fits?

Tell us your background and we will map it to the right entry point — including saying so when a cheaper programme is the better fit. A counsellor replies within one working day.