numerical rank
Exact rank counts the nonzero singular values, but in floating-point arithmetic almost nothing is exactly zero. Rounding and measurement noise smear a true zero singular value into something tiny but positive, so the naive count would always report full rank. Numerical rank fixes this by counting the singular values that are genuinely large.
Given a tolerance tol, the numerical rank is the number of singular values strictly greater than tol: r = number of k with sigma_k > tol. The tolerance is chosen to sit above the noise floor — a common default is tol = max(m, n) times the largest singular value times machine epsilon, so that values explainable by rounding are discarded.
This is exactly the rank you should trust in practice, and singular-value perturbation theory is what justifies it. Because each singular value moves by at most ||E||_2 under a perturbation E, a clear gap in the singular-value spectrum is stable: noise below the gap cannot lift a small singular value across it. The size of that gap is your confidence margin.
Numerical rank underlies low-rank approximation, rank-revealing factorizations, and regularization of ill-posed problems. When the singular values decay smoothly with no clear gap, the numerical rank is genuinely ambiguous, and that ambiguity is itself useful information: it tells you the problem has no sharp intrinsic dimension.
Three singular values clear the tolerance; the 1e-15 one is rounding noise and is correctly ignored.
Numerical rank is meaningful only relative to a tolerance — quote the tol you used. The same matrix can be rank 3 at one tolerance and rank 5 at a looser one; the singular-value gap, not the raw count, is what should drive your decision.