LESSON 66 · DEEPER PYTHON29 min

Compare Floating-Point Results with math.isclose

Use explicit numerical tolerances for approximate float results, including comparisons near zero.

Example check: Worked and guided code samples are checked against CPython 3.13. Practice uses Pyodide 0.29.5 (Python 3.13) in your browser. How we check lessons

Video lesson: Compare Floating-Point Results with math.isclose

The preview is stored on this site. YouTube loads only when you press Play. Watch on YouTube

Read the video transcript

Select a timestamp to watch that moment on YouTube.

  1. 00:00

    Imagine a program that calculates a sensor reading in one place and receives the expected reading from another. Both are displayed as 0.3, but an equality check says they differ. We can reproduce this with the familiar expression 0.1 plus 0.2. The important question is not whether Python forgot how to add. We need to ask what values are actually stored, what exact equality means, and what amount of numerical error the application permits. In this lesson we will print the real difference, use math.isclose with a relative tolerance, then meet a case that relative tolerance alone cannot handle: comparison with zero.

  2. 00:41

    We will finish a function that makes the tolerance visible to callers and rejects invalid negative settings. You can run every stage in the browser console on the lesson page. Notice that this is a lesson about approximate numerical calculations. Prices and accounting amounts can require decimal representation and explicit cent- rounding rules; our separate Decimal lesson covers that different contract. Here, the goal is to make an approximation policy explicit instead of changing the data until a test happens to pass. The first line is False. Equality compares the stored float values exactly, and those values are not

  3. 01:21

    identical. The second line tells us how far apart they are: about five and a half times ten to the minus seventeen. That is tiny compared with a value near three tenths, but it is not zero. The last line displays the computed value with one decimal place. It says 0.3 because formatting rounds the text shown to us. It has not modified left, and it has not changed the equality result. Many decimal fractions have repeating binary expansions. A float stores a finite approximation, so the stored result of one expression can land on a different representable value from another expression that looks

  4. 02:02

    equivalent in decimal notation. A common attempted fix is round(left, 1) == round(right, 1). That can hide a genuine error if one decimal place is too coarse for the measurement. We should choose a comparison rule from the required accuracy, not from the number of digits that happens to make this one test green. math.isclose compares the absolute difference with an allowance computed from the two magnitudes. The decimal-sum difference is much smaller than a relative allowance of one part in a billion, so the first answer is True. The second pair differs by one out of roughly one hundred.

  5. 02:43

    A tolerance of one part in a thousand is too narrow, so it returns False. The third pair also differs by one unit, but one is a very small fraction of a million, so it returns True. This is why one unexplained epsilon is not appropriate for every calculation. Relative tolerance is useful when acceptable error scales with the size of the result. It might fit a physical measurement with a proportional accuracy specification. If the application says that even large values must agree within a fixed number of units, use an absolute tolerance instead. math.isclose has a default relative tolerance, but using that default silently still makes a policy choice.

  6. 03:27

    At a code boundary, state the tolerance and units. In tests, include values that should definitely pass and values that should definitely fail; otherwise a tolerance can be made too generous simply to satisfy one example. The function has keyword-only tolerance parameters, so a call that overrides a threshold has to name what it is changing. It checks for negative settings and then delegates the numerical comparison to math.isclose. The first call handles the small binary approximation from our opening example. The next two calls describe a hypothetical sensor whose readings within one hundredth of zero are treated as zero.

  7. 04:09

    With abs_tol set to 0.01, 0.005 qualifies, while 0.02 does not. Without that absolute threshold, a nonzero value is not normally close to zero by relative tolerance alone: the relative allowance shrinks along with the magnitude. This does not mean 0.01 is a universal noise floor. Another sensor, a scientific calculation, or a financial total needs its own specification. Also observe that the function returns a Boolean; it does not alter either input or manufacture a rounded result. Keep calculation and comparison separate.

  8. 04:50

    If the product ultimately needs a displayed value with a certain number of decimal places, format it after the calculation rather than using display formatting as the comparison policy. The task on the site starts with exact equality; replace it with this explicit rule and use the checker to examine multiple boundaries. The assertions test both acceptance and rejection. The familiar decimal sum should pass, and a sensor reading of 0.009 is within the illustrative absolute threshold. A reading of 0.02 is beyond it, so the next assertion requires False. The pair one hundred and one hundred one must also fail under a much tighter

  9. 05:33

    relative allowance; this catches an implementation that returns True for everything. Exact equality remains a valid special case and should still pass. Finally, the negative tolerance is rejected with ValueError, rather than being treated as a mysterious comparison result. Notice what these tests cannot do: they cannot choose the right tolerance for your real project. That requires knowing the input's units, measurement accuracy, and consequence of a false match. A single passing example says very little about whether a threshold is safe at other magnitudes. Try changing the absolute threshold from 0.01 to 0.001 and predict which near-zero

  10. 06:16

    assertions change before running them. Then compare a large pair under two relative tolerances and explain why the decision changes. This is the skill to carry forward: inspect the numerical difference, state the allowable error, and verify the boundary where the answer flips. On the lesson page, complete the task and quiz after experimenting with those inputs.

Understand the concept

Python floats store finite binary approximations. Many decimal fractions have no finite binary expansion. Consequently, 0.1 + 0.2 can be a stored value slightly above the stored value for 0.3. Equality is working correctly: it asks whether the two stored values are exactly equal. It cannot decide whether the difference is acceptable to a measurement or calculation. Print the values and their difference before changing the comparison.

math.isclose(a, b) compares the difference against a tolerance. A relative tolerance scales with the magnitude of the inputs: the same proportional error permits a larger absolute difference for large values. Python provides a default, but a real application should state its own allowed error. For example, rel_tol=0.001 means about one part in a thousand. A tolerance is a domain decision, not a universal repair for every failed equality test.

Near zero, relative tolerance is insufficient for a nonzero value because the allowable difference also approaches zero. An absolute tolerance expresses a threshold in the same units as the values. For a sensor whose noise is 0.01 units, abs_tol=0.01 might make sense; that number should not be copied into an unrelated money calculation. math.isclose also treats NaN as close to no value, including itself, and infinity as close only to itself.

The checked function close_measurements accepts two numbers plus keyword-only relative and absolute tolerances. It rejects negative tolerances, calls math.isclose, and returns a Boolean. Tests cover the classic decimal sum, a nonzero reading near zero, two distant readings, and exact equality. Do not round both operands first: rounding can erase a difference your actual tolerance should detect.

  • Binary floats approximate decimal fractions
  • Equality compares stored values exactly
  • Relative tolerance scales with magnitude
  • Absolute tolerance handles near-zero comparisons

See it step by step

Read the code, predict the output, then compare it with the result.

01. See the stored difference

PYTHON
print(0.1 + 0.2 == 0.3)
print(format((0.1 + 0.2) - 0.3, '.17g'))
EXPECTED OUTPUT
False
5.5511151231257827e-17

Exact equality detects a tiny difference between stored values; == is not malfunctioning.

02. State a relative tolerance

PYTHON
import math
print(math.isclose(0.1 + 0.2, 0.3, rel_tol=1e-9))
print(math.isclose(100, 101, rel_tol=0.001))
EXPECTED OUTPUT
True
False

The first difference is below its allowance; one unit around one hundred exceeds one part in a thousand.

03. Use an absolute threshold at zero

PYTHON
import math
print(math.isclose(0.005, 0.0))
print(math.isclose(0.005, 0.0, abs_tol=0.01))
EXPECTED OUTPUT
False
True

A relative-only comparison cannot admit a nonzero value as zero; the explicit threshold can.

A closer look

Follow the reasoning, inspect each result, then try the suggested changes in the console below.

01 / 03

Reproduce a surprising equality result

Begin with the question the program actually asks. Equality compares the stored results exactly. The decimal expressions on screen look equivalent, but 0.1, 0.2, and 0.3 each have binary approximations. Addition chooses another representable float. That makes a tiny difference visible to == even though a person expects approximately the same decimal quantity.

Run the code and inspect the scientific-notation difference rather than declaring Python arithmetic broken. The last line deliberately rounds for display only; it does not change the values involved in the earlier comparison. When a test fails, printing an informative difference helps distinguish a harmless approximation from a genuinely wrong calculation.

PYTHON
left = 0.1 + 0.2
right = 0.3
print(left == right)
print(format(left - right, '.17g'))
print(format(left, '.1f'))
EXPECTED OUTPUT
False
5.5511151231257827e-17
0.3
Follow the reasoning
  1. The displayed one-decimal value hides a small stored difference.
  2. Exact equality sees the actual stored values.
  3. The difference gives a scale for choosing a tolerance.
02 / 03

Compare differences at different magnitudes

Relative tolerance is proportional to the larger magnitude of the two operands. A difference of one unit may be tiny for values around a million and substantial for values around a hundred. This is why an unexplained hard-coded epsilon applied to every number is unreliable. Choose a scale that matches the domain's resolution or stated measurement error.

The code uses the same absolute difference in two comparisons. It prints True only for the large pair because one part in a thousand permits a much wider absolute difference there. Notice that this does not make the large result universally correct: if the application requires precision to the cent, its tolerance would be stricter.

PYTHON
import math
print(math.isclose(100.0, 101.0, rel_tol=0.001))
print(math.isclose(1000000.0, 1000001.0, rel_tol=0.001))
EXPECTED OUTPUT
False
True
Follow the reasoning
  1. The first pair differs by about one percent.
  2. The second pair differs by about one millionth.
  3. The same one-unit gap has different relative meaning.
03 / 03

Add a zero rule and validate settings

A nonzero value is not normally close to zero under a relative-only comparison. Add an absolute tolerance only when a real threshold exists, such as known sensor noise. The checked function forwards both tolerances to math.isclose and rejects negative settings before the library call. Keeping the settings visible at the call site makes the rule auditable.

Verify both sides of the threshold: 0.005 qualifies under a 0.01 absolute allowance, while 0.02 does not. Also check that a one-unit error near 100 is not accidentally accepted under a tight relative allowance. These boundary checks are more useful than repeatedly testing 0.1 + 0.2 alone.

PYTHON
import math
def close_measurements(left, right, *, rel_tol=1e-9, abs_tol=0.0):
    if rel_tol < 0 or abs_tol < 0:
        raise ValueError('tolerances must be nonnegative')
    return math.isclose(left, right, rel_tol=rel_tol, abs_tol=abs_tol)
print(close_measurements(0.005, 0.0, abs_tol=0.01))
print(close_measurements(0.02, 0.0, abs_tol=0.01))
print(close_measurements(100.0, 101.0, rel_tol=0.001))
EXPECTED OUTPUT
True
False
False
Follow the reasoning
  1. Validate before calculating.
  2. Pass explicit tolerances into math.isclose.
  3. Check values just inside and outside the domain threshold.

Try it in Python

Edit the example and run it. Python starts in your browser the first time you click Run.

Python console

Ready to run
Need input()? Add one value per line
Ctrl / ⌘ + Enter to run
OUTPUT
Your output appears here.
Your turnComplete close_measurements with math.isclose and the supplied tolerances. Raise ValueError for a negative rel_tol or abs_tol. The prepared calls must both print True.
Need a hint?

Import math. Reject negative tolerances first, then return math.isclose(left, right, rel_tol=rel_tol, abs_tol=abs_tol).

Complete the task and select Check task to verify your code.

    Quick quiz

    Three questions. You can change your answers and try again.

    01.Why can 0.1 + 0.2 == 0.3 be false?

    02.How can a small nonzero reading count as close to zero?

    03.Who decides the acceptable tolerance?

    Typical mistakes

    Everyone meets these errors. See what causes them and how to fix them.

    Exact equality for approximate calculations

    COMMON MISTAKE
    return left == right
    FIX
    return math.isclose(left, right, rel_tol=1e-9)

    What happens: 0.1 + 0.2 compares unequal to 0.3.
    Use a documented tolerance when the inputs are approximate calculations.

    Relative tolerance alone at zero

    COMMON MISTAKE
    math.isclose(0.005, 0.0, rel_tol=0.01)
    FIX
    math.isclose(0.005, 0.0, abs_tol=0.01)

    What happens: The result is False even if this sensor reading is negligible.
    A near-zero threshold is an absolute difference in the measured units.

    Browse this lesson’s problem fixes

    Sources and further reading