Hands-on fractions.Fraction vs float for exact ratios: real ops/s and exactness checks, measured on Linux localhost today in this hands-on lab for SREs.
4 min read
Exact rational math with fractions.Fraction vs float (and a Decimal reference arm). This lab reports ops/s on Linux localhost, plus exactness checks for ratios that cancel in theory and diverge in binary float.
Related links:
Lab honesty (1 Oct 2026 IST): Python 3.13.5 on this Linux box. Affiliates: 0. Differentiates from Decimal-vs-float sum (lab 85) — focus here is Fraction rationals and exact canceling products, not money decimals.
Verdict up front: float sum 0.1×50 k ~119415247 ops/s; Decimal ~20714012; Fraction ~2420034. Ratio product ∏ i/(i+1): float ~30498219 ops/s vs Fraction ~1311467 — Fraction stays exact (1/5001).
| Arm | Pattern |
|---|---|
sum([0.1]*n) | binary float tenths |
sum([Fraction(1,10)]*n) | exact tenths |
sum([Decimal("0.1")]*n) | decimal reference |
| ∏ i/(i+1) float vs Fraction | canceling ratios |
| Fraction(float) vs Fraction("0.1") | construction paths |
Each arm measures wall time with time.perf_counter over seven rounds and reports p50 ops/s so one noisy sample cannot dominate the lead table.
Script: lab-evidence/122-fractions-vs-float/results/run_lab.py. Same machine, same interpreter — no invented timings.
| Arm | ops/s |
|---|---|
| sum float 0.1 | 119415247 |
| sum Decimal "0.1" | 20714012 |
| sum Fraction 1/10 | 2420034 |
| product ratio float | 30498219 |
| product ratio Fraction | 1311467 |
| Fraction from float accum | 710385 |
| Fraction from str accum | 524856 |
Float leads throughput. Fraction pays for arbitrary-precision gcd work on every multiply/add.
1/5001; float 0.00019996000799840023 (last bits ≠ float(Fraction) = 0.0001999600079984003).Fraction(1, 10) or Fraction("0.1") — not Fraction(0.1), which freezes binary residue into a huge numerator/denominator.On this run, sum([0.1]*50000) may print as a tidy 5000.0. That cosmetic print does not make float a rational type; the canceling product still drifts from exact 1/5001.
| Goal | Construct |
|---|---|
| Exact tenth | Fraction(1, 10) / Fraction("0.1") |
| From binary float | Fraction(x).limit_denominator() |
| Money / currency | prefer Decimal (see lab 85) |
| Hot telemetry means | stay on float |
If you only need “close enough” for dashboards, float’s ~119415247 ops/s is the right tool. If auditors ask for exact 1/5001, Fraction’s slower path is the one that answers without hand-waving.
Fraction(0.1) capturing binary float residue.limit_denominator.Evidence: summary.json, summary.txt under lab-evidence/122-fractions-vs-float/results/.
One Linux box, CPython 3.13.5, pure-Python Fraction. Not mpmath, not GPU reductions, not multi-process.
Float wins throughput (~119415247 ops/s on the 0.1 sum); Fraction is slower (~2420034) but keeps exact rationals like 1/5001. Use Fraction when exact ratios matter; use float when speed dominates and binary error is acceptable.
Lab evidence
Ran the package lab on Linux localhost with CPython 3.13.5 on 1 Oct 2026 IST. Benchmarked seven rounds at p50: float, Decimal, and Fraction sums plus canceling products. Verified the exact Fraction result 1/5001 and float drift; affiliates 0.
Summary1 Oct 20263 min read
Hands-on gc.collect cost empty vs cyclic garbage lab: real collect latency plus reclaim counts, measured on Linux localhost today in this lab for SREs.
Hands-on1 Oct 20264 min read
Hands-on1 Oct 20264 min read