Почему сравнение float чисел может быть некорректным и как с этим бороться?
Что проверяют
Понимаете ли, что двоичное представление не хранит большинство десятичных дробей точно, и умеете ли выбрать правильный вид допуска. Для аналитики и моделей это ещё и вопрос про воспроизводимость: получатся ли одинаковые числа у двух пайплайнов.
Как отвечать
Начать с причины: число хранится как мантисса, умноженная на степень двойки. У 0.1 в двоичной системе бесконечная периодическая запись, поэтому в памяти лежит ближайшее представимое значение, и сумма приближений не равна приближению суммы:
0.1 + 0.2 == 0.3 # False
0.1 + 0.2 # 0.30000000000000004Дальше про сравнение. Прямое равенство годится только для целых, точно представимых значений. В остальных случаях сравнивают с допуском, и допуск нужен относительный: абсолютный порог, разумный для единиц, бессмыслен для миллионов, где сам шаг представления крупнее порога. В Python для этого есть готовое:
math.isclose(a, b, rel_tol=1e-9, abs_tol=1e-12)Относительный допуск работает почти везде, абсолютный нужен отдельно для сравнения с нулём, потому что возле нуля относительная погрешность теряет смысл. Для массивов то же самое дают numpy.isclose и numpy.allclose.
Когда точность обязательна, меняют тип. Деньги считают в Decimal или в целых минимальных единицах. Ключи, по которым соединяют таблицы и группируют, тоже не должны быть дробными: округляйте до целого шага заранее.
Где спотыкаются
Берут один абсолютный порог на все случаи. На больших значениях он строже соседних представимых чисел, и равенство не срабатывает даже там, где числа совпадают по смыслу.
Забывают про NaN. Он не равен ничему, включая себя, поэтому проверка на совпадение молча даёт «различаются», а сортировка и группировка обходятся с ним по-разному в разных библиотеках.
Третье: считают, что одинаковый код даёт одинаковый результат. Сложение не ассоциативно, поэтому другой порядок агрегации, другое число потоков или другая разрядность (float32 против float64) меняют последние разряды. Признак, посчитанный в обучении и в сервисе разными путями, попадает по разные стороны порога, и предсказание уезжает.