Ляп в Питоне: x + 1.0 < x

1,00
р.
При смешивании типов возможно неочевидное поведение:
>>> x = (1 << 53) + 1 >>> x + 1.0 < x True
Казалось бы, что x + 1.0 не меньше просто x, поэтому ожидаемый результат сравнения False, но возвращается True. Почему?

Ответ
Кратко: 9007199254740992.0 < 9007199254740993
При сложении, числа к общему типу приводятся в Питоне. x (int) конвертируется во float так как 1.0 это float. x непредставим точно в виде float, поэтому выбирается ближайшее (меньшее здесь) представимое число: x -> float(x-1). Поэтому сумма получается неточной:
x+1.0 -> float(x-1)+1.0 -> (x) -> float(x-1)
Сравнение float < int происходит точно. Значение x (int) математически больше x+1.0 (float).

Целые числа в Питоне имеют бесконечную точность (могут быть сколь угодно большими). Но вещественные числа, такие как создаваемые 1.0 константой в исходном коде, представлены типом float— числами с плавающей точкой.
x слагаемое во float здесь превращается:
otherwise, if either argument is a floating point number, the other is converted to floating point
ещё до вычисления суммы:
the numbers are converted to a common type and then added together
>>> x 9007199254740993 >>> float(x) == float(x-1) == 9007199254740992.0 True >>> x+1.0 == float(x) + 1 == float(x-1) + 1 == float(x) == float(x-1) True
На IEEE 754 платформах, где Python float соответствует числам с двойной точностью:
>>> sys.float_info.radix 2 >>> sys.float_info.mant_dig 53 >>> float(1<<53).hex() # точно представлено '0x1.0000000000000p+53' >>> float((1<<53) + 1).hex() # непредставимо точно, округляется '0x1.0000000000000p+53' >>> (float((1<<53) + 1) + 1.0).hex() # двойное округление '0x1.0000000000000p+53' <br>Вывод показывает, что float(x) и x + 1.0 представлены как одно и то же число: float(x-1). Подробнее см. вопрос Отображение числа 9223372036854775807.
x является int, поэтому величина (1<<53)+1 точно представлена (9007199254740993).<br>Сравнение математически точно происходит без приведения типов:
The objects do not need to have the same type. ... Within the limits of the types involved, they compare mathematically (algorithmically) correct without loss of precision.
>>> 9007199254740992.0 < 9007199254740993 True
Вот так x + 1.0 может быть меньше x в Питоне.