Python 如何比较 'int' 和 'float' 对象?
Posted
技术标签:
【中文标题】Python 如何比较 \'int\' 和 \'float\' 对象?【英文标题】:How does Python compare 'int' to 'float' objects?Python 如何比较 'int' 和 'float' 对象? 【发布时间】:2020-01-31 14:31:44 【问题描述】:The documentation 关于数字类型声明:
Python 完全支持混合算术:当一个二元算术运算符具有不同数值类型的操作数时,具有“较窄”类型的操作数被扩大到另一个,其中整数比浮点窄,浮点比复数窄. 混合类型数量之间的比较使用相同的规则。
以下行为支持这一点:
>>> int.__eq__(1, 1.0)
NotImplemented
>>> float.__eq__(1.0, 1)
True
但是对于大整数,似乎会发生其他事情,因为除非明确转换为float
,否则它们不会比较相等:
>>> n = 3**64
>>> float(n) == n
False
>>> float(n) == float(n)
True
另一方面,对于 2 的幂,这似乎不是问题:
>>> n = 2**512
>>> float(n) == n
True
由于文档暗示 int
被“扩大”(我假设转换/转换?)到 float
我希望 float(n) == n
和 float(n) == float(n)
是相似的,但上面的 n = 3**64
示例建议不同。那么 Python 使用什么规则来比较 int
和 float
(或一般的混合数字类型)?
使用 Anaconda 的 CPython 3.7.3 和 PyPy 7.3.0 (Python 3.6.9) 进行测试。
【问题讨论】:
您可能刚刚达到浮点精度限制,float(3**64)
不完全等于 到 int(3**64)
。
float(2**512)
,另一方面,可以精确表示,尽管它要大得多,因为它是 2 的幂。尾数只需要 1 位即可获得全精度,而指数只需要 9。
我理解所有这些行为,但文档指出“较窄”类型,在本例中为 int
,将扩大到另一种,在本例中为 float
。因此,由于往返行程,精度损失不应该有任何问题;我希望 float(n) == n
在内部进行处理,以便 r.h.s.转换为float
,即类似于float(n) == float(n)
。
如果我没记错的话,Python,或者至少它的一些实现,会不遗余力地准确比较值。当3**64
与float(n)
进行比较时,它不会转换为float
。而是将3**64
的精确数学值与float(n)
的精确数学值进行比较。由于它们不同,因此结果为False
。当您将3**64
转换为float
时,它会转换为float
类型中可表示的值,从而引入一些错误。不幸的是,文档中的措辞说float
比int
“更宽”。一般不能代表所有int
值……
github.com/python/cpython/blob/master/Objects/… 似乎负责浮点和整数比较。它当然不仅仅是将 int 转换为浮点数并进行比较,但我的 C 阅读能力还不足以知道它到底在做什么。
【参考方案1】:
language specification on value comparisons 包含以下段落:
内置数字类型(数字类型 —
int, float, complex
)和标准库类型fractions.Fraction
和decimal.Decimal
的数量可以在它们的类型内部和它们之间进行比较,但限制是复数不支持顺序比较。在所涉及类型的限制范围内,它们在数学(算法)上比较正确而不会损失精度。
这意味着当比较两种数字类型时,会比较这些对象表示的实际(数学)数字。例如numeral16677181699666569.0
(即3**34
)代表数字16677181699666569,即使在“浮动空间”中,这个数字和16677181699666568.0
(3**34 - 1
)之间没有区别,它们确实代表不同的数字.由于浮点精度有限,在 64 位架构上,值 float(3**34)
将存储为 16677181699666568,因此它表示与整数 16677181699666569
不同的数字。出于这个原因,我们有float(3**34) != 3**34
,它可以在不损失精度的情况下进行比较。
此属性对于保证数字类型的equivalence relation 中的transitivity 很重要。如果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
【讨论】:
以上是关于Python 如何比较 'int' 和 'float' 对象?的主要内容,如果未能解决你的问题,请参考以下文章