Programming with Python › Python Setup and First Programs › Day 46
Hands-on lab — Day 46: Numbers, Math, and Precision
- ← Back to the Day 46 lesson
- Open the hands-on files on GitHub — clone or download them from the public labs repository
- Local path in your clone:
labs/sections/programming-with-python/day-046-numbers-math-and-precision/
Commands
Setup
cd labs/sections/programming-with-python/day-046-numbers-math-and-precision Run
python3 examples/precision_demo.py
python3 starter/precision_demo.py Test
bash tests/run_tests.sh File tree
examples/precision_demo.py expected-output/FIELDS.md expected-output/sample-run.txt metadata.yml README.md requirements/README.md security.md starter/numbers-worksheet.md starter/precision_demo.py tests/run_tests.sh troubleshooting.md
Lab README
Day 046 lab — Precision and Money Math
Lesson
- Lesson title: Numbers, Math, and Precision
- Day number: 46 of 365
- Lesson article: https://ai-roadmap-365.github.io/day-046-numbers-math-and-precision
- Lab files: everything you need is in this directory — follow “How to run” below.
- Browse the course locally: from the repository root, this lab also appears in the course website at
/labs/day-046-numbers-math-and-precisionwhen the site is running.
Purpose
Day 46's lesson explains how Python stores numbers and where floating-point
precision quietly goes wrong. This lab makes it concrete: you run a small
program that reproduces the famous 0.1 + 0.2 != 0.3 trap, compares floats
the safe way, and shows the difference between adding up a shopping cart in
float (subtly wrong) and in Decimal (exactly right). You finish able to
choose the correct numeric type for a task instead of hoping the defaults work.
Learning objectives
- Reproduce and explain the classic floating-point rounding error.
- Compare two computed floats safely with
math.iscloseinstead of==. - Compute an exact currency total with the
decimalmodule. - Use floor division (
//) and modulo (%) to make change from whole cents. - Call the everyday
mathfunctions (sqrt,floor,ceil).
Prerequisites
- The Day 46 lesson (read it first — it explains every idea this lab exercises).
- Python 3 installed and runnable as
python3(Day 43 set this up). - A terminal and any text editor for completing the starter exercises.
Supported operating systems
- macOS — fully supported (executed on macOS, Apple Silicon, Python 3.14).
- Linux — fully supported (any distribution with Python 3 and bash).
- Windows — run the Python program directly with
python; run the.shtest script under WSL or Git Bash. The program's output is identical on every platform because it is pure arithmetic.
Hardware requirements
Any computer that can run Python 3. The program uses a trivial amount of CPU and memory and touches neither disk nor network.
Required software
python3≥ 3.8 (executed on 3.14). Standard library only —decimalandmathship with Python.bashto run the test script (preinstalled on macOS and Linux).
Free and open-source options
Everything here is free and open-source: CPython and its standard library
carry no cost, no account, and no API key. There is nothing to install beyond
Python itself — the exact-money math uses the built-in decimal module, not a
paid library.
Installation
None beyond Python. Confirm Python is present, then change into this directory:
python3 --version
cd labs/sections/programming-with-python/day-046-numbers-math-and-precision
File structure
day-046-numbers-math-and-precision/
├── README.md ← you are here
├── metadata.yml ← machine-readable lab metadata
├── starter/
│ ├── precision_demo.py ← YOUR working file (5 exercises)
│ └── numbers-worksheet.md ← record your observed values here
├── examples/
│ └── precision_demo.py ← completed reference implementation
├── tests/
│ └── run_tests.sh ← automated checks (exit 0 = pass)
├── expected-output/
│ ├── sample-run.txt ← real captured run of the reference program
│ └── FIELDS.md ← the lines that must appear on every platform
├── requirements/
│ └── README.md ← dependency statement (Python 3 only)
├── troubleshooting.md
└── security.md
How to run
From this directory:
## 1. See the finished result first
python3 examples/precision_demo.py
## 2. Your task: complete the five exercises in the starter, then run it
python3 starter/precision_demo.py
## 3. Check your work
bash tests/run_tests.sh
What the commands do
python3 examples/precision_demo.py— runs the reference program: it prints the exact float value of0.1 + 0.2, showsmath.isclosefixing the comparison, contrasts afloatcart total with an exactDecimaltotal, makes change with//and%, and calls a fewmathfunctions.python3 starter/precision_demo.py— the same program with five values blanked out; each numbered exercise comment names the exact expression to fill in. Edit the file in any text editor and replace each marked line.bash tests/run_tests.sh— runs the reference program and a set ofpython3 -cassertions, checking that the float error is real, thatmath.isclosetreats the sum as0.3, that theDecimalcart total is exactly28.78, and that725 // 100 == 7and725 % 100 == 25. Exits 0 on success, non-zero on any failure.
Expected output
See expected-output/sample-run.txt — a
real captured run. The opening section looks like this:
============================================================
1. The float trap: 0.1 + 0.2 != 0.3
============================================================
0.1 + 0.2 = 0.30000000000000004
Is it exactly 0.3? False
0.1 stored as = 0.1000000000000000055511151231257827021181583404541015625
0.1 + 0.2 stored = 0.3000000000000000444089209850062616169452667236328125
Because the program does pure arithmetic on fixed numbers, your output will
match this exactly on any standard Python build.
expected-output/FIELDS.md lists every line that
must appear.
Validation steps
- Run
python3 examples/precision_demo.py— it must finish without an error. - Confirm the float line reads
0.30000000000000004andIs it exactly 0.3? False. - Confirm the cart section charges
$28.78(the exactDecimaltotal). - Complete the five exercises in
starter/precision_demo.py; running it should now print the same key values as the reference. - Run the tests (next section) — all checks must pass.
Tests
bash tests/run_tests.sh
Expected final line: 9 checks, 0 failure(s). The command exits 0 on success
and non-zero on any failure, so it can run in CI.
Cleanup
Nothing to clean up: the program writes no files and changes no settings. To
reset your work, restore the starter from git:
git checkout -- starter/precision_demo.py.
Troubleshooting
See troubleshooting.md for the full list — the == 0.3
"bug" that is not a bug, Decimal(0.1) vs Decimal("0.1"), // vs /,
missing python3, and Windows notes.
Security notes
See security.md. Short version: the program makes no network
calls and needs no privileges — and, importantly, Python's random module is
not cryptographically secure. Use the secrets module for tokens,
passwords, and anything that must be unguessable.
Extension exercises
- Add a line that sums
[Decimal("0.1")] * 10and confirms it equalsDecimal("1.0")exactly — then try the same with floats and watch it drift. - Import
fractions.Fractionand show thatFraction(1, 3) * 3 == 1exactly, where1/3 * 3in float does not always round-trip. - Change the cart prices and the paid amount, and confirm the change-making
loop still hands out the right coins with
//and%.
Navigation
- Previous day: Day 45 — Variables, Types, and Assignment
(
labs/sections/programming-with-python/day-045-.../). - Next day: Day 47 — Input, Output, and f-strings
(
labs/sections/programming-with-python/day-047-input-output-and-f-strings/, to be written).
Expected output
FIELDS.md
# Expected output — required fields on every platform
The reference program `examples/precision_demo.py` is deterministic: it does
pure arithmetic on fixed numbers, so **its output is identical on macOS,
Linux, and Windows** for any standard CPython build. The full captured run is
in [`sample-run.txt`](sample-run.txt). These specific lines must appear:
| Section | Line that must appear | Why it is fixed |
| --- | --- | --- |
| 1. Float trap | `0.1 + 0.2 = 0.30000000000000004` | IEEE 754 double arithmetic is specified bit-for-bit |
| 1. Float trap | `Is it exactly 0.3? False` | The stored sum is slightly above 0.3 |
| 1. Float trap | `0.1 stored as = 0.1000000000000000055511151231257827021181583404541015625` | The exact value of the nearest double to 0.1 |
| 2. isclose | `math.isclose(total, 0.3) -> True` | The two values are within the default tolerance |
| 3. Money | `float sum = 28.779999999999998` | Float rounding error accumulates across the cart |
| 3. Money | `Decimal sum = 28.78` | Decimal is exact for base-10 fractions |
| 3. Money | `Charge the customer: $28.78` | Quantized to the nearest cent |
| 4. Change | `Change owed: 725 cents ($7.25)` | Integer arithmetic, no rounding |
| 4. Change | `7 x dollar (100c)` | `725 // 100 == 7` |
| 5. math | `math.sqrt(2) = 1.4142135623730951` | The nearest double to the square root of 2 |
## What may legitimately differ
Nothing in this program depends on the machine, the clock, or the network, so
there are **no platform-specific values** to account for. If any line above
differs on your machine, you are almost certainly on a very unusual Python
build; note it and move on. (Contrast this with the Day 001 lab, whose output
is entirely machine-specific.)
sample-run.txt
============================================================
1. The float trap: 0.1 + 0.2 != 0.3
============================================================
0.1 + 0.2 = 0.30000000000000004
Is it exactly 0.3? False
0.1 stored as = 0.1000000000000000055511151231257827021181583404541015625
0.1 + 0.2 stored = 0.3000000000000000444089209850062616169452667236328125
============================================================
2. Comparing floats safely with math.isclose
============================================================
total == 0.3 -> False
math.isclose(total, 0.3) -> True
round(total, 2) == 0.3 -> True
============================================================
3. Shopping cart: float (wrong) vs Decimal (exact)
============================================================
Items: [19.99, 5.99, 2.5, 0.1, 0.2]
float sum = 28.779999999999998
Decimal sum = 28.78
float == 28.78? False
Decimal == 28.78? True
Charge the customer: $28.78
============================================================
4. Making change with // and %
============================================================
Price $12.75, paid $20.00
Change owed: 725 cents ($7.25)
7 x dollar (100c)
1 x quarter (25c)
0 x dime (10c)
0 x nickel (5c)
0 x penny (1c)
Remaining after change: 0 cents
============================================================
5. The math module: sqrt, floor, ceil
============================================================
math.sqrt(2) = 1.4142135623730951
math.floor(3.7) = 3
math.ceil(3.2) = 4
math.pi = 3.141592653589793
Done. See numbers-worksheet.md to record what you observed.
Source files
examples/precision_demo.py (3641 bytes)
#!/usr/bin/env python3
"""Day 046 lab — Precision and Money Math (completed reference).
Runs five short demonstrations of how Python handles numbers and where
floating-point precision bites:
1. The classic float trap: 0.1 + 0.2 is not exactly 0.3.
2. Comparing floats safely with math.isclose.
3. A shopping-cart total in float (wrong) vs Decimal (exact).
4. Making change with floor division (//) and modulo (%).
5. A few essentials from the math module (sqrt, floor, ceil).
Everything here uses only the Python standard library — no installs, no
network, no API keys. Run it with:
python3 examples/precision_demo.py
"""
from decimal import Decimal, ROUND_HALF_UP
import math
def section(title):
"""Print a labelled section header so the output reads clearly."""
print()
print("=" * 60)
print(title)
print("=" * 60)
def demo_float_trap():
section("1. The float trap: 0.1 + 0.2 != 0.3")
total = 0.1 + 0.2
print(f"0.1 + 0.2 = {total}")
print(f"Is it exactly 0.3? {total == 0.3}")
# A float prints its shortest round-tripping form, which hides the error.
# Decimal(float) shows the exact value actually stored in the 64 bits.
print(f"0.1 stored as = {Decimal(0.1)}")
print(f"0.1 + 0.2 stored = {Decimal(total)}")
def demo_isclose():
section("2. Comparing floats safely with math.isclose")
total = 0.1 + 0.2
print(f"total == 0.3 -> {total == 0.3}")
print(f"math.isclose(total, 0.3) -> {math.isclose(total, 0.3)}")
print(f"round(total, 2) == 0.3 -> {round(total, 2) == 0.3}")
def demo_money():
section("3. Shopping cart: float (wrong) vs Decimal (exact)")
prices_float = [19.99, 5.99, 2.50, 0.10, 0.20]
prices_decimal = [Decimal(str(p)) for p in prices_float]
float_total = sum(prices_float)
decimal_total = sum(prices_decimal)
print(f"Items: {prices_float}")
print(f"float sum = {float_total!r}")
print(f"Decimal sum = {decimal_total}")
print(f"float == 28.78? {float_total == 28.78}")
print(f"Decimal == 28.78? {decimal_total == Decimal('28.78')}")
# Money is rounded to the nearest cent, half rounding up, the way a till does.
rounded = decimal_total.quantize(Decimal("0.01"), rounding=ROUND_HALF_UP)
print(f"Charge the customer: ${rounded}")
def demo_change():
section("4. Making change with // and %")
# Work in whole cents (integers) so there is no float error at all.
price_cents = 1275 # $12.75
paid_cents = 2000 # $20.00
change = paid_cents - price_cents
print(f"Price ${price_cents / 100:.2f}, paid ${paid_cents / 100:.2f}")
print(f"Change owed: {change} cents (${change / 100:.2f})")
coins = [("dollar", 100), ("quarter", 25), ("dime", 10),
("nickel", 5), ("penny", 1)]
remaining = change
for name, value in coins:
count = remaining // value # how many whole coins of this size fit
remaining = remaining % value # what is left over after handing them out
print(f" {count} x {name} ({value}c)")
print(f"Remaining after change: {remaining} cents")
def demo_math_module():
section("5. The math module: sqrt, floor, ceil")
print(f"math.sqrt(2) = {math.sqrt(2)}")
print(f"math.floor(3.7) = {math.floor(3.7)}")
print(f"math.ceil(3.2) = {math.ceil(3.2)}")
print(f"math.pi = {math.pi}")
def main():
demo_float_trap()
demo_isclose()
demo_money()
demo_change()
demo_math_module()
print()
print("Done. See numbers-worksheet.md to record what you observed.")
if __name__ == "__main__":
main()
metadata.yml (593 bytes)
lesson_id: D046
day: 46
kind: python-program
languages: [python]
setup_commands:
- cd labs/sections/programming-with-python/day-046-numbers-math-and-precision
run_commands:
- python3 examples/precision_demo.py
- python3 starter/precision_demo.py
test_commands:
- bash tests/run_tests.sh
cleanup_commands:
- 'git checkout -- starter/precision_demo.py # optional: reset your work'
requires_network: false
requires_api_key: false
estimated_minutes: 30
last_executed: '2026-07-12'
executed_on: 'macOS (Apple Silicon), Python 3.14.0, bash tests/run_tests.sh → 9 checks, 0 failure(s).'
requirements/README.md (866 bytes)
# Dependencies — Day 046 lab
**Only Python 3 itself.** Everything this lab uses ships in the CPython
standard library — there is nothing to `pip install`:
- `python3` ≥ 3.8 (any modern CPython; the lab was executed on 3.14).
- The `decimal` module (standard library) — exact base-10 arithmetic.
- The `math` module (standard library) — `sqrt`, `floor`, `ceil`, `isclose`.
- `bash` (to run `tests/run_tests.sh`) — preinstalled on macOS and Linux.
Check your Python version:
```bash
python3 --version
```
If that prints `Python 3.x.y`, you are ready. There is deliberately no
`requirements.txt` here: the point of the lab is that precision-correct money
math needs no third-party library at all — the batteries are already
included. Course 3 of the course previews **NumPy**, which you *would*
install for array math, but this lab does not need it.
starter/numbers-worksheet.md (1773 bytes)
# Numbers worksheet — Day 046
Fill this in as you complete the exercises in `precision_demo.py`. Everything
you need appears in the program's own output; copy the real values you see.
## 1. The float trap
Run `python3 examples/precision_demo.py` (or your completed starter) and record:
- `0.1 + 0.2` prints as: `________________________________`
- Is it exactly equal to `0.3`? (`True` / `False`): `______`
- The exact value of `0.1` actually stored in the 64 bits (the long
`Decimal(0.1)` line): `________________________________________________`
One sentence, in your own words, on *why* `0.1` cannot be stored exactly:
```
______________________________________________________________________
```
## 2. Comparing floats safely
- `total == 0.3` gives: `______`
- `math.isclose(total, 0.3)` gives: `______`
Which of these should you use when comparing two computed floats, and why?
```
______________________________________________________________________
```
## 3. The exact Decimal total
For the cart `[19.99, 5.99, 2.50, 0.10, 0.20]`:
- `float` sum prints as: `________________________________`
- `Decimal` sum prints as: `______`
- The amount the customer is charged: `$______`
Why did you build the Decimals from **strings** (`Decimal("19.99")`) rather
than from floats (`Decimal(19.99)`)?
```
______________________________________________________________________
```
## 4. Floor division vs true division
For a change amount of `725` cents:
- `725 / 100` (true division, `/`) gives: `______`
- `725 // 100` (floor division, `//`) gives: `______`
- `725 % 100` (modulo, `%`) gives: `______`
One sentence on when you would reach for `//` and `%` instead of `/`:
```
______________________________________________________________________
```
starter/precision_demo.py (2611 bytes)
#!/usr/bin/env python3
"""Day 046 lab starter — Precision and Money Math.
Complete the five numbered exercises below. Each one names the exact tool to
use. Run the file after each change to see your progress:
python3 starter/precision_demo.py
The finished reference version lives in examples/precision_demo.py — try each
exercise yourself first, then compare. Record your answers in
starter/numbers-worksheet.md.
"""
from decimal import Decimal
import math
def main():
# ------------------------------------------------------------------
# Exercise 1: show the float error.
# Add 0.1 and 0.2 with the + operator, store it in `total`, and print it.
# Then print whether `total` is exactly equal to 0.3 using ==.
# Replace the two placeholder lines below.
total = 0.0 # <-- Exercise 1: change 0.0 to 0.1 + 0.2
print(f"0.1 + 0.2 = {total}")
print(f"Equals 0.3 exactly? {total == 0.3}")
# ------------------------------------------------------------------
# Exercise 2: compare floats safely.
# Set `close` to the result of math.isclose(total, 0.3).
# It should be True even though the == check above is False.
close = None # <-- Exercise 2: change None to math.isclose(total, 0.3)
print(f"math.isclose(total, 0.3) = {close}")
# ------------------------------------------------------------------
# Exercise 3: compute an exact total with Decimal.
# Build Decimals from the STRING form of each price (Decimal("19.99")),
# never from the float, then sum them. The exact answer is 28.78.
prices = ["19.99", "5.99", "2.50", "0.10", "0.20"]
decimal_total = Decimal("0") # <-- Exercise 3: sum Decimal(p) for p in prices
print(f"Decimal cart total = {decimal_total}")
print(f"Exactly 28.78? {decimal_total == Decimal('28.78')}")
# ------------------------------------------------------------------
# Exercise 4: use // (floor division).
# A customer is owed 725 cents in change. How many whole dollars (100c)
# is that? Use // to get the integer count.
change_cents = 725
dollars = 0 # <-- Exercise 4: change 0 to change_cents // 100
print(f"{dollars} whole dollars in {change_cents} cents")
# ------------------------------------------------------------------
# Exercise 5: use % (modulo).
# After handing out the whole dollars, how many cents are left over?
# Use % to get the remainder.
leftover_cents = 0 # <-- Exercise 5: change 0 to change_cents % 100
print(f"{leftover_cents} cents left over after the dollars")
if __name__ == "__main__":
main()
tests/run_tests.sh (2846 bytes)
#!/usr/bin/env bash
# Tests for the Day 046 lab. Run from the lab directory:
# bash tests/run_tests.sh
#
# Runs the reference program and confirms the numeric facts it demonstrates:
# the Decimal cart total is exact, math.isclose treats 0.1+0.2 as 0.3, and
# floor division / modulo give the expected whole numbers. No network access.
set -u
lab_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
failures=0
checks=0
check() {
local label="$1" ok="$2"
checks=$((checks + 1))
if [ "${ok}" = "yes" ]; then
echo " ok: ${label}"
else
echo " FAIL: ${label}"
failures=$((failures + 1))
fi
}
# --- 1. The reference program runs cleanly and prints its sections. --------
echo "Running examples/precision_demo.py ..."
if output="$(python3 "${lab_dir}/examples/precision_demo.py" 2>&1)"; then
check "program exits successfully" "yes"
else
check "program exits successfully" "no"
echo "${output}" | sed 's/^/ /'
fi
echo "${output}" | grep -q "0.30000000000000004" \
&& check "shows the exact float result of 0.1 + 0.2" "yes" \
|| check "shows the exact float result of 0.1 + 0.2" "no"
echo "${output}" | grep -q "Charge the customer: \$28.78" \
&& check "charges the exact Decimal total of \$28.78" "yes" \
|| check "charges the exact Decimal total of \$28.78" "no"
echo "${output}" | grep -q "7 x dollar" \
&& check "making change gives 7 whole dollars" "yes" \
|| check "making change gives 7 whole dollars" "no"
# --- 2. Direct numeric assertions with python3 -c. -------------------------
echo "Checking numeric facts directly ..."
python3 -c "assert (0.1 + 0.2) != 0.3, 'expected float error'" \
&& check "0.1 + 0.2 != 0.3 in float (the trap is real)" "yes" \
|| check "0.1 + 0.2 != 0.3 in float (the trap is real)" "no"
python3 -c "import math; assert math.isclose(0.1 + 0.2, 0.3), 'isclose should be True'" \
&& check "math.isclose(0.1 + 0.2, 0.3) is True" "yes" \
|| check "math.isclose(0.1 + 0.2, 0.3) is True" "no"
python3 -c "from decimal import Decimal; prices=['19.99','5.99','2.50','0.10','0.20']; assert sum(Decimal(p) for p in prices) == Decimal('28.78'), 'Decimal total must be exact'" \
&& check "Decimal cart total is exactly 28.78" "yes" \
|| check "Decimal cart total is exactly 28.78" "no"
python3 -c "assert 725 // 100 == 7, 'floor division wrong'; assert 725 % 100 == 25, 'modulo wrong'" \
&& check "725 // 100 == 7 and 725 % 100 == 25" "yes" \
|| check "725 // 100 == 7 and 725 % 100 == 25" "no"
python3 -c "from decimal import Decimal; assert Decimal('0.1') + Decimal('0.2') == Decimal('0.3'), 'Decimal from strings must be exact'" \
&& check "Decimal('0.1') + Decimal('0.2') == Decimal('0.3')" "yes" \
|| check "Decimal('0.1') + Decimal('0.2') == Decimal('0.3')" "no"
echo
echo "${checks} checks, ${failures} failure(s)."
[ "${failures}" -eq 0 ]
Troubleshooting
Troubleshooting — Day 046 lab
My == 0.3 check is False and I think that is a bug
It is not a bug — it is the whole point of the lab. In IEEE 754 double
precision, 0.1, 0.2, and 0.3 are each stored as the nearest available
binary fraction, and 0.1 + 0.2 lands a hair above the stored 0.3. Never
compare two computed floats with ==. Use math.isclose(a, b) (True when
the values are within a small tolerance) or round both sides to the number of
decimals you care about: round(a, 2) == round(b, 2).
Decimal("0.1") is exact but Decimal(0.1) is not
This trips up everyone once. Decimal(0.1) takes the float 0.1 — which is
already the wrong value 0.1000000000000000055... — and copies that error
into the Decimal. Decimal("0.1") parses the string "0.1" and stores
exactly one tenth. Rule: build money Decimals from strings (or from
integers), never from float literals. Run this to see the difference:
python3 -c "from decimal import Decimal; print(Decimal(0.1)); print(Decimal('0.1'))"
python3: command not found
Your system may expose Python only as python. Try python --version; if it
reports 3.x, substitute python for python3 in every command. On macOS,
install a current Python from python.org or Homebrew (brew install python);
on Debian/Ubuntu, sudo apt install python3.
SyntaxError or IndentationError in my starter
Python is indentation-sensitive: each statement inside def main(): must be
indented with the same spaces. If you pasted code, mixed tabs and spaces are
the usual culprit — re-indent with spaces only, or restore the starter from
git (git checkout -- starter/precision_demo.py) and edit just the marked
lines.
ModuleNotFoundError: No module named 'decimal' or 'math'
Both modules are part of the standard library and cannot normally be missing.
This almost always means a file in your working directory is named math.py
or decimal.py and is shadowing the real module — rename your file and try
again.
The tests fail on the change section
tests/run_tests.sh checks that 725 // 100 == 7 and 725 % 100 == 25. If
you edited examples/precision_demo.py, make sure floor division uses //
(two slashes) and not / (one slash, which gives 7.25). Restore the
reference file from git if needed.
Windows: bash is not recognized
Run the tests under WSL, or run the Python program directly with
python examples\precision_demo.py (the program itself is pure Python and is
fully cross-platform; only the .sh test runner needs a POSIX shell).
Security notes
Security notes — Day 046 lab
-
What the program does: pure arithmetic on numbers written into the source. It makes no network connections, reads no files, writes no files, and needs no elevated privileges. Read it first (it is short and commented), as you should with any script.
-
randomis not cryptographically secure. The lesson introduces Python'srandommodule for simulations, samples, and shuffles. It uses a Mersenne Twister generator that is fast and statistically good but predictable: given enough outputs, an attacker can reconstruct its internal state and predict every future value. Never userandomfor anything that must be unguessable — passwords, session tokens, API keys, password-reset links, one-time codes, or nonces. -
Use the
secretsmodule instead for security. The standard library'ssecretsmodule draws from the operating system's cryptographically secure random source. Reach for it whenever unpredictability protects something:import secrets token = secrets.token_urlsafe(32) # a safe URL-friendly token code = secrets.randbelow(1_000_000) # a safe 0-999999 one-time codeRule of thumb:
randomfor games, tests, and data sampling;secretsfor anything a stranger should not be able to guess. -
Money math is a correctness-and-trust issue. Silent float rounding errors in financial code are a real-world source of reconciliation bugs and disputes. Using
Decimal(or integer cents) for currency is not just tidy — it prevents a class of defects that erode user trust and can have legal and audit consequences.