值比较语言规范 包含以下段落:
内置数字类型(数字类型-int,float,complex
)和标准库类型fractions.Fraction
和decimal.Decimal
的数字可以在其类型内部和跨类型进行比较,但复数不支持顺序比较。在涉及的类型限制内,它们在算法上进行正确的数学比较而没有精度损失。
这意味着当比较两个数值类型时,将比较这些对象所表示的实际(数学)数字。例如,
numeral 16677181699666569.0
(即
3**34
)表示数字16677181699666569,尽管在“浮点空间”中,这个数字和
16677181699666568.0
(
3**34 - 1
)之间没有区别,但它们代表不同的数字。由于浮点精度有限,在64位架构上,值
float(3**34)
将存储为16677181699666568,因此它代表的数字与整数数字
16677181699666569
不同。因此,我们有
float(3**34) != 3**34
,它可以在不丢失精度的情况下进行比较。
这个属性很重要,以保证数值类型的等价关系传递性。如果将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
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
float(3**64)
并不完全等于int(3**64)
。 - decezefloat(2**512)
可以被准确地表示,尽管它更大,因为它是2的幂。尾数只需要1位即可完全精确,指数只需要9位。 - chepnerint
,会被扩展到另一个类型,例如float
。因此,在往返转换中不应该有精度损失的问题;我预期float(n) == n
会在内部处理,使得右侧被转换成float
,即类似于float(n) == float(n)
。 - a_guest3**64
和float(n)
进行比较时,它不会转换为float
类型。相反,精确数学值3**64
与精确数学值float(n)
进行比较。由于它们不同,结果是False
。当你将3**64
转换为float
类型时,它会转换为一个可表示为float
类型的值,引入了一些误差。在文档中的措辞不幸地说float
比int
"更宽"。通常,它不能表示所有的int
值... - Eric Postpischil