При смешивании типов возможно неочевидное поведение
>>> 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'
Вывод показывает, что float(x) и x + 1.0 представлены как одно и то же число: float(x-1). Подробнее см. вопрос Отображение числа 9223372036854775807
x является int, поэтому величина (1<<53)+1 точно представлена (9007199254740993).
Сравнение математически точно происходит без приведения типов
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 в Питоне.
Комментариев нет:
Отправить комментарий