Skip to content

← Dtypes and Numerics step 5 of 6

Easy Primitives

Compare floating-point tensors with tolerances

Jump to the task

Floating-point calculations can introduce rounding differences even when the mathematical results are equal. For example, on CPU with float32:

import torch

x = torch.tensor(2.0, dtype=torch.float32).sqrt() ** 2
print(x.item())                              # 1.9999998807907104
print(bool(x == 2.0))                        # False
print(torch.allclose(x, torch.tensor(2.0)))  # True

Float32 cannot represent the square root of 2 exactly. Squaring its stored approximation can therefore give a nearby value rather than exactly 2. The size of rounding differences depends on the calculation, dtype and backend.

Absolute and relative tolerance

For finite values, torch.allclose(a, b) checks every element against:

abs(a - b) <= atol + rtol * abs(b)

atol is an absolute allowance in the same units as the values. rtol scales the allowance with the magnitude of the reference b. Since the formula uses b, swapping the arguments can change the answer.

The defaults are atol=1e-8 and rtol=1e-5. These are comparison defaults, not universal error bounds for float32. Choose tolerances for the accuracy your application needs and the errors you expect.

a = torch.tensor([1e-9])
b = torch.tensor([0.0])
print(torch.allclose(a, b, atol=1e-8, rtol=0.0))  # True
print(torch.allclose(a, b, atol=0.0, rtol=1e-5))  # False

Here the reference is zero, so the relative term contributes nothing. Setting either tolerance to zero can be intentional: absolute-only and relative-only comparisons are both useful when they match the requirement.

Choose the comparison you need

torch.allclose returns one Python boolean. torch.isclose returns a boolean tensor identifying which elements are close. NaNs do not compare close by default; matching infinities are handled separately from the finite formula above.

Exact equality is appropriate when exact agreement is the requirement, including comparisons of indices, labels or deliberately preserved values. Some floating-point arithmetic is exact too. Do not add a tolerance merely because an input has a floating dtype, or assume all computed floats differ.

For test assertions, torch.testing.assert_close provides useful diagnostics and dtype-dependent defaults. Review those tolerances for your calculation rather than treating them as a guarantee of acceptable accuracy.

Your task

def close_enough(a: torch.Tensor, b: torch.Tensor, atol: float, rtol: float) -> bool

The tests provide same-shaped, finite float32 tensors and nonnegative tolerances. Return a Python bool indicating whether every element of a is close to the corresponding element of b, using the supplied atol and rtol. Do not replace them with fixed defaults.