Спецификация языка для сравнения значений содержит следующий абзац:
Номера встроенных числительных c типов (Numeri c Типы - int, float, complex
) и из типов стандартных библиотек fractions.Fraction
и decimal.Decimal
можно сравнивать внутри и между их типами с ограничением на то, что комплексные числа не поддерживают сравнение порядка. В пределах используемых типов они математически (алгоритмически) корректно сравниваются без потери точности .
Это означает, что при сравнении двух типов чисел c фактическое ( математические) числа, представленные этими объектами, сравниваются. Например, цифра 16677181699666569.0
(то есть 3**34
) представляет число 16677181699666569, и хотя в «float-space» нет разницы между этим числом и 16677181699666568.0
(3**34 - 1
) они действительно представляют разные цифры. Из-за ограниченной точности с плавающей запятой в 64-битной архитектуре значение float(3**34)
будет сохранено как 16677181699666568 и, следовательно, оно представляет собой число, отличное от целого числа 16677181699666569
. По этой причине у нас есть float(3**34) != 3**34
, который выполняет сравнение без потери точности.
Это свойство важно для того, чтобы гарантировать транзитивность отношения эквивалентности нумерации c типов. Если сравнение от int
до float
даст аналогичные результаты, как если бы объект int
будет преобразован в объект float
, тогда транзитивное отношение будет недействительным:
>>> class Float(float):
... def __eq__(self, other):
... return super().__eq__(float(other))
...
>>> a = 3**34 - 1
>>> b = Float(3**34)
>>> c = 3**34
>>> a == b
True
>>> b == c
True
>>> a == c # transitivity demands that this holds true
False
* float.__eq__
реализация с другой стороны, которая рассматривает представленные математические числа, не нарушает это требование:
>>> a = 3**34 - 1
>>> b = float(3**34)
>>> c = 3**34
>>> a == b
True
>>> b == c
False
>>> a == c
False
В результате отсутствия транзитивности порядок следующего списка не будет изменен сортировкой (так как все последовательные числа кажутся равными):
>>> class Float(float):
... def __lt__(self, other):
... return super().__lt__(float(other))
... def __eq__(self, other):
... return super().__eq__(float(other))
...
>>> numbers = [3**34, Float(3**34), 3**34 - 1]
>>> sorted(numbers) == numbers
True
При использовании float
, с другой стороны, порядок меняется на обратный:
>>> numbers = [3**34, float(3**34), 3**34 - 1]
>>> sorted(numbers) == numbers[::-1]
True