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