Three flavours, one root cause. (1) A sum or product of clearly positive numbers
comes out negative or absurdly small. (2) Array maths crashes with
numpy.core._exceptions.UFuncTypeError or behaves like text — because the array IS
text (dtype='<U21'). (3) A comparison that must be true is false:
np.float64(0.1) + 0.2 == 0.3 prints False, and an equality check between two
"identical" result arrays fails.
Loading data from files or lists, summing money columns, comparing a rewritten pipeline against the old one's output. Freshers hit flavour 2 the first time one stray string rides along in a numeric list, and flavour 3 the first time they write a test for numeric code. Linked module: 0.4 (NumPy).
Every array has ONE dtype, and it silently decides range and behaviour. Flavour 1
is overflow: a small integer dtype (int32 from some sources on Windows, int8/int16
from image or sensor data) wraps around past its maximum instead of erroring —
np.int32 tops out near 214 crore, one big UPI-scale sum away. Flavour 2 is
promotion: np.array([1, 2, "3"]) makes EVERYTHING a string, because one text
element forces the common type; the numbers are gone and arr * 2 no longer means
arithmetic. Flavour 3 is float representation: 0.1 has no exact binary form, so
tiny errors accumulate and exact == becomes a coin toss. In every flavour the
first diagnostic is the same reflex: print(arr.dtype, arr.shape) — the dtype line
names the problem before any theory does.
Flavour 1: cast up before the big operation — arr.astype(np.int64) (or float64)
— and re-run the sum; for money, store paise as int64 rather than rupees as floats.
Flavour 2: find and fix the stray non-number at the SOURCE (the file or list), then
rebuild with an explicit dtype=np.float64; casting a string array does not recover
values that were mis-parsed. Flavour 3: replace exact equality with tolerance —
np.isclose(a, b) element-wise or np.allclose(a, b) for whole arrays — and use
the same in tests.
Print dtype together with shape at every pipeline step — one reflex, two bugs
caught. Pass explicit dtype= when creating arrays from external data instead of
trusting inference. Never compare floats with == in code or tests; allclose is
the default. Keep money in integer paise, and treat any negative total from
positive inputs as an overflow alarm, not a data mystery.
Q: "A colleague's revenue total went negative on a big file. Walk me through it."
Strong answer: name integer overflow with a small dtype, show the one-line proof
(print(arr.dtype)), give the cast-up fix, and volunteer the money rule — integers
in paise, floats never — plus allclose for float comparisons. Covering all three
dtype traps in one answer marks you as someone who has cleaned real data.