Многих новичков (и не очень новичков) может фрустрировать тот факт, что:
>>> 0.1 + 0.1 + 0.1 - 0.3
5.551115123125783e-17
😱 Нет, ваш Python не сломан, так и должно быть. Много слов уже сказано про представление чисел с точкой в компьютерах. Большинство наших вычислений производится согласно стандарту IEEE754 (👉читайте подбронее https://habr.com/post/112953/).
Если коротко, то числа в компьютере квантированы, то есть если упрощенно, они не непрерывны. Факт: числа 0.3 для компьютера не существует, зато есть самое ближайшее к нему число (3 - 5.551115123125783e-17).
Поэтому советуют никогда не сравнивать float-ы на точное равенство, когда один или оба операнда получены из вычислений:
>>> 0.3 == 0.3
True
>>> (0.1 + 0.1 + 0.1) == 0.3
False
Лучше сравнивать их приблизительно (модуль разницы с неким малым числом):
>>> def floats_equal(x, y, eps=1e-10): return abs(x - y) < eps
>>> floats_equal( 0.1 + 0.1 + 0.1, 0.3 )
True
💸 Если ваше приложение связано с подсчетом денег, точность всегда критична, иначе ваш баланс не сойдется. Для этого давно придуман модуль decimal (https://docs.python.org/3.6/library/decimal.html). В нем есть класс Decimal – фактически он представляет собой число с фиксированной точкой.
>>> from decimal import *
>>> Decimal(0.1)+Decimal(0.1)+Decimal(0.1)-Decimal(0.3)
Decimal('1.11022302E-17')
😤 Что за дела? Опять тоже самое! Дело в том, что нельзя получить "качественный" Decimal из обычного числа (мы помним, что 0.3 – не существует и будет подобрано ближайшее).
💡 Фишка в том, что нужно сначала преобразовать число в строку (str), а потом передать в Decimal. Тогда он будет явно знать какое число из реального мира мы хотим сохранить:
>>> Decimal(str(0.1)) + Decimal(str(0.1)) + Decimal(str(0.1)) - Decimal(str(0.3))
Decimal('0.0')
#python