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) == nfloat(n) == float(n) 是相似的,但上面的 n = 3**64 示例建议不同。那么 Python 使用什么规则来比较 intfloat(或一般的混合数字类型)?


使用 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**64float(n) 进行比较时,它不会转换为float。而是将3**64 的精确数学值与float(n) 的精确数学值进行比较。由于它们不同,因此结果为False。当您将3**64 转换为float 时,它会转换为float 类型中可表示的值,从而引入一些错误。不幸的是,文档中的措辞说floatint“更宽”。一般不能代表所有int值…… github.com/python/cpython/blob/master/Objects/… 似乎负责浮点和整数比较。它当然不仅仅是将 int 转换为浮点数并进行比较,但我的 C 阅读能力还不足以知道它到底在做什么。 【参考方案1】:

language specification on value comparisons 包含以下段落:

内置数字类型(数字类型 — int, float, complex)和标准库类型 fractions.Fractiondecimal.Decimal 的数量可以在它们的类型内部和它们之间进行比较,但限制是复数不支持顺序比较。在所涉及类型的限制范围内,它们在数学(算法)上比较正确而不会损失精度

这意味着当比较两种数字类型时,会比较这些对象表示的实际(数学)数字。例如numeral16677181699666569.0(即3**34)代表数字16677181699666569,即使在“浮动空间”中,这个数字和16677181699666568.03**34 - 1)之间没有区别,它们确实代表不同的数字.由于浮点精度有限,在 64 位架构上,值 float(3**34) 将存储为 16677181699666568,因此它表示与整数 16677181699666569 不同的数字。出于这个原因,我们有float(3**34) != 3**34,它可以在不损失精度的情况下进行比较。

此属性对于保证数字类型的equivalence relation 中的transitivity 很重要。如果intfloat 比较会得到类似的结果,就像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' 对象?的主要内容,如果未能解决你的问题,请参考以下文章

Java中的long类型和Long类型比较大小

字符串和数据类型转换

《Python数据分析》笔记1 ——Numpy

Python如何将'int'与'float'对象进行比较?

Python循环仅绘制分类变量

python如何区分bool和int