numpy floating point errors: 0.1 + 0.2, isclose, arange (tested)
Floating point errors in numpy come from binary floats: 0.1 + 0.2 is 0.30000000000000004, so compare with np.isclose (mind its default atol=1e-08 for tiny values), and use np.linspace instead of float-step np.arange, which can include the stop value.
1. The common surprises, measured
Run with python b1.py:
import sys
import numpy as np
print("numpy", np.__version__, "python", sys.version.split()[0])
a = np.array([0.1, 0.2, 0.3])
print("0.1 + 0.2 == 0.3:", a[0] + a[1] == a[2])
print("0.1 + 0.2 =", repr(a[0] + a[1]))
print("np.isclose(0.1 + 0.2, 0.3):", np.isclose(a[0] + a[1], a[2]))
print("np.arange(1, 1.3, 0.1):", np.arange(1, 1.3, 0.1))
print("np.linspace(1, 1.2, 3):", np.linspace(1, 1.2, 3))
print("isclose(1e-10, 0):", np.isclose(1e-10, 0), "| with atol=0:", np.isclose(1e-10, 0, atol=0))
print("isclose(1e-10, 2e-10):", np.isclose(1e-10, 2e-10), "| with atol=0:", np.isclose(1e-10, 2e-10, atol=0))
print("allclose([1, 2], [1, 2 + 1e-9]):", np.allclose([1.0, 2.0], [1.0, 2.0 + 1e-9]))
print("np.float32(0.1) == 0.1:", np.float32(0.1) == 0.1, "| float32 value:", np.float32(0.1).item())
print("np.float32(0.1) == np.float64(0.1):", np.float32(0.1) == np.float64(0.1))
print("np.round(2.5), np.round(3.5):", np.round(2.5), np.round(3.5))
print("np.sum(ten 0.1s):", np.sum(np.full(10, 0.1)), "| loop:", end=" ")
t = 0.0
for _ in range(10):
t += 0.1
print(t)
Output:
numpy 2.5.4 python 3.13.5
0.1 + 0.2 == 0.3: False
0.1 + 0.2 = np.float64(0.30000000000000004)
np.isclose(0.1 + 0.2, 0.3): True
np.arange(1, 1.3, 0.1): [1. 1.1 1.2 1.3]
np.linspace(1, 1.2, 3): [1. 1.1 1.2]
isclose(1e-10, 0): True | with atol=0: False
isclose(1e-10, 2e-10): True | with atol=0: False
allclose([1, 2], [1, 2 + 1e-9]): True
np.float32(0.1) == 0.1: True | float32 value: 0.10000000149011612
np.float32(0.1) == np.float64(0.1): False
np.round(2.5), np.round(3.5): 2.0 4.0
np.sum(ten 0.1s): 1.0 | loop: 0.9999999999999999
2. What each line means and what to do
0.1 + 0.2 == 0.3is False because 0.1, 0.2 and 0.3 have no exact binary representation; the sum is0.30000000000000004. This is IEEE 754 floating point, not a numpy bug. Compare withnp.isclose/np.allclose(ormath.isclosefor scalars), never==.np.arange(1, 1.3, 0.1)returned 4 values; the last one prints as 1.3 (it is1.3000000000000003), althoughstopis supposed to be excluded: the step count is computed from the rounded floats. Usenp.linspace(start, stop, num)when you know how many points you want.np.isclosetreats tiny numbers as equal: its defaultatol=1e-08makes1e-10"close" to both 0 and2e-10(twice its value). For values far below 1, passatol=0(or a scale-appropriate atol) and rely onrtol.np.float32(0.1) == 0.1is True in numpy 2, butnp.float32(0.1) == np.float64(0.1)is False: the Python float is converted to float32 before comparing (NEP 50, see numpy 2 breaking changes), while a float64 array or scalar keeps full precision. The float32 value is really 0.10000000149011612.np.round(2.5)is 2.0 andnp.round(3.5)is 4.0: numpy rounds halves to the nearest even number (like Python'sround).np.sumgave exactly 1.0 for ten 0.1s, while a plain+=loop gave0.9999999999999999: numpy uses pairwise summation, which accumulates less rounding error. Results can therefore differ slightly between numpy and a loop, or between array layouts.
To find where a calculation produces inf or nan (overflow, divide by zero), see numpy errstate. For exact decimal arithmetic (money), use Python's decimal module instead of floats.
Related: numpy polyfit RankWarning ยท numpy LinAlgError: Singular matrix.
Frequently asked questions
Why is 0.1 + 0.2 not equal to 0.3 in numpy?
The decimals have no exact binary representation; the float64 sum is 0.30000000000000004. Compare with np.isclose or np.allclose instead of ==.
Why does np.arange include the stop value?
With a float step the number of elements is computed from rounded values, so np.arange(1, 1.3, 0.1) returned [1. 1.1 1.2 1.3]. Use np.linspace(start, stop, num).
Why does np.isclose say 1e-10 equals 2e-10?
The default atol=1e-08 dominates for tiny numbers. Pass atol=0 (or an appropriate scale) so only rtol applies.
Written with AI assistance. Measured on 2026-10-23 with numpy 2.5.4 and Python 3.13.5 on Linux. The script was run verbatim and the output pasted unedited. PlainTables is our extension; numpy is free.