Вы можете использовать десятичный модуль , чтобы получить представление о том, что «скрывается» между числами с плавающей запятой, такими как 0.3
:
>>> from decimal import Decimal
>>> Decimal(0.3)
Decimal('0.299999999999999988897769753748434595763683319091796875')
Обратите внимание, что Python 2.7 изменил способЧисла с плавающей запятой пишутся (как работает repr(f)
), так что теперь она показывает самую короткую строку, которая даст такое же число с плавающей запятой, если вы сделаете float(s)
.Это означает, что repr(0.3) == '0.3'
в Python 2.7, но repr(0.3) == '0.29999999999999999'
в более ранних версиях.Я упоминаю об этом, поскольку это может еще больше запутать ситуацию, когда вы действительно захотите увидеть, что стоит за числами.
Используя десятичный модуль, мы можем увидеть ошибку в вычислении с плавающей запятой:
>>> (Decimal(2.0) - Decimal(1.1)) / Decimal(0.3) - Decimal(3)
Decimal('-1.85037170771E-16')
Здесь мы можем ожидать (2.0 - 1.1) / 0.3 == 3.0
, но есть небольшая ненулевая разница.Однако, если вы выполняете вычисления с обычными числами с плавающей запятой, вы получите ноль:
>>> (2 - 1.1) / 0.3 - 3
0.0
>>> bool((2 - 1.1) / 0.3 - 3)
False
Результат округляется где-то по пути, поскольку 1.85e-16 не равен нулю:
>>> bool(-1.85037170771E-16)
True
Я не уверен точно, где происходит это округление.
Что касается завершения цикла в целом, то я могу предложить одну подсказку: для чисел с плавающей запятой менее 2 53, IEEE 754 может представлять все целые числа :
>>> 2.0**53
9007199254740992.0
>>> 2.0**53 + 1
9007199254740992.0
>>> 2.0**53 + 2
9007199254740994.0
Пробел между представленными числами составляет 2 от 2 53 до 2 54 , какпоказано выше.Но если ваше i
является целым числом, меньшим 2 53 , то i - 1
также будет представимым целым числом, и вы в конечном итоге наберете 0.0
, что в Python считается ложным.