Два очень маленьких числа кажутся одинаковыми для задачи, но math.isclose возвращает False. Обычно дело не в том, что Python «сломался», а в том, какой допуск указан в вызове. На трех учебных парах разберем разницу между относительным и абсолютным допуском и проверим, почему сравнение с нулем требует отдельного решения.

Все пары и допуски ниже придуманы для демонстрации поведения функции. Это не нормы точности измерительного прибора и не допустимая ошибка реального исследования. Код выполнен в Python 3.12.14, документация взята из ветви 3.12. Результат True означает, что выполнено выбранное вычислительное условие. Он не доказывает равенство величин и не говорит о качестве данных.

Как функция решает, достаточно ли близки числа

В документации math.isclose приведено условие для обычных конечных чисел: abs(a-b) <= max(rel_tol * max(abs(a), abs(b)), abs_tol). Относительная часть масштабируется по большему модулю двух чисел. Абсолютная часть задает порог разности в тех же единицах, что a и b. Используется максимум двух частей, а не их сумма.

По умолчанию rel_tol=1e-9 и abs_tol=0.0. Если сравнивать ненулевое x с нулем, одна относительная часть при rel_tol меньше 1 положительного результата не даст: разность abs(x) больше rel_tol * abs(x). Поэтому положительный абсолютный допуск около нуля нужно обосновать задачей, а не добавлять только ради True.

  Учебная пара       По умолчанию / выбранный режим  
  0 и 1e-10       False / True  
  1000 и 1000.001       False / True  
  1 и 1.1       False / False  
Авторская схема, не интерфейс. Данные и условия учебные, вымышленные. isclose: что считается близким?. Около нуля 0 и 1e-10 По умолчанию False; выбранный режим True. При большем масштабе 1000 и 1000.001 По умолчанию False; выбранный режим True. Различие остается 1 и 1.1 False в обоих показанных режимах. Выбранный режим: rel_tol=1e-6; abs_tol=1e-9.
Авторская схема, не интерфейс. Данные и условия учебные, вымышленные. isclose: что считается близким?. Около нуля 0 и 1e-10 По умолчанию False; выбранный режим True. При большем масштабе 1000 и 1000.001 По умолчанию False; выбранный режим True. Различие остается 1 и 1.1 False в обоих показанных режимах. Выбранный режим: rel_tol=1e-6; abs_tol=1e-9.

Для всех трех строк выбран один режим: rel_tol=1e-6, abs_tol=1e-9. Не читайте таблицу так, будто абсолютный допуск объясняет каждую строку отдельно: во второй паре решает относительная часть. Первая строка, наоборот, показывает проблему сравнения с нулем.

Полный код: три пары и особые значения

default означает результат с параметрами по умолчанию, selected — результат с явно заданными допусками. В конце отдельно показаны NaN и положительная бесконечность, чтобы True и False не принимали за проверку пригодности произвольных данных к анализу.

from math import isclose

pairs = [(0.0, 1e-10), (1000.0, 1000.001), (1.0, 1.1)]
results = []
for a, b in pairs:
    default = isclose(a, b)
    selected = isclose(a, b, rel_tol=1e-6, abs_tol=1e-9)
    results.append((default, selected))
    print(f"{a!r} vs {b!r}: default={default}; selected={selected}")
print("nan_close =", isclose(float("nan"), float("nan")))
print("inf_close =", isclose(float("inf"), float("inf")))
assert results == [(False, True), (False, True), (False, False)]
assert not isclose(float("nan"), float("nan"))
assert isclose(float("inf"), float("inf"))
0.0 vs 1e-10: default=False; selected=True
1000.0 vs 1000.001: default=False; selected=True
1.0 vs 1.1: default=False; selected=False
nan_close = False
inf_close = True

В первой паре разность 1e-10 меньше выбранного abs_tol=1e-9, поэтому получается True. Во второй разность около 0.001 сравнивается с относительным порогом около 0.001000001 и укладывается в него. Для 1 и 1.1 разность около 0.1 заметно больше порога около 0.0000011, поэтому остается False. Десятичные записи здесь нужны, чтобы показать масштаб: код работает с float, а не с точной десятичной арифметикой.

Документация отдельно оговаривает особые значения: NaN не считается близким даже самому себе, бесконечность близка только самой себе с тем же знаком. Показанное inf_close=True не делает бесконечность пригодным наблюдением. Состав и допустимость входных данных проверяют отдельно, этот фрагмент не заменяет валидатор.

Как выбрать и описать допуски

  Параметр или решение       Что записать в методике  
  rel_tol       Безразмерный относительный допуск и обоснование масштаба сравнения.  
  abs_tol       Абсолютный допуск с единицами сравниваемых величин; особенно важен около нуля.  
  Состав пар       Какие значения сравнивают и что делают с пропусками и особыми значениями.  
  Смысл результата       Выполнено ли условие допуска; это не статистический тест и не доверительный интервал.  

Сначала разберитесь с единицами. Один и тот же числовой абсолютный допуск для метров и миллиметров означает разную физическую величину. Относительный допуск проблему нуля не снимает. Не поднимайте пороги подряд, пока нужные пары не дадут True, а потом не выдавайте этот выбор за заранее заданное правило.

Сравнение близости также не исправляет исходное число и не округляет его для таблицы. О представлении значений в float рассказывает официальное руководство Python. Если задача состоит именно в сохранении исходной десятичной записи, используйте отдельный разбор Decimal после float: у сравнения по допуску и округления разные цели.

  • До просмотра результатов определите смысл сравнения, единицы и допустимые отклонения.
  • Сохраните оба параметра явно, даже если один из них совпадает с умолчанием.
  • Проверьте нуль, положительные и отрицательные конечные значения в своем диапазоне: три пары не покрывают все входы.
  • Отдельно опишите пропуски и особые значения. Не заменяйте проверку данных одним вызовом isclose.

Если нужно согласовать вычислительное сравнение с исследовательской задачей, на странице помощи со статистическим анализом можно обсудить состав консультации. Передайте исходные пары, единицы и причину выбора порогов. Само увеличение числа совпадений не является улучшением качества измерений и не подтверждает содержательную гипотезу.