Python浮点数除法的相等性

3
使用Python 3,以下代码如何返回True?
a = 2/3
b = 4/6
print(a == b)

我有一个算法,需要对一个数字列表进行排序,其中每个数字都是x/y形式的,其中x和y是整数。(y != 0)。
我担心除法的数值精度会导致不稳定性和任意排序,例如上面的情况。这是相关评论的一个例子。但是,根据这个例子和更大的整数,似乎这不是一个问题。
Python是否从b的分子和分母中删除2的公共因素,并保留a和b不仅仅是浮点数的信息?

抱歉,我没能理解你的问题。难道不是吗?这里有什么需要关注的呢? - Keerthana Prabhakaran
{btsdaf} - tokenizer_fsj
1
{btsdaf} - Kevin
3个回答

4

Python遵循IEEE 754浮点数规范。* (64位)IEEE浮点数本质上是一种基于科学计数法的形式,如下所示:

  • 一位用于符号(正或负)
  • 53位用于尾数或有效数字,包括隐含的前导位。
  • 11位用于指数。

将浮点数值乘以二或任何二的幂,只会影响指数,而不影响尾数。** 因此,它通常本身是一个相当“稳定”的操作,因此2/3应该产生与4/6相同的结果。 然而,IEEE浮点数仍然存在以下问题:

  • 大多数运算不满足结合律(例如,一般情况下 (a * b) * c != a * (b * c))。
  • 更复杂的运算不要求正确舍入(然而,正如 Tim Peters 指出的那样,除法显然不是一个“更复杂”的运算,因此将被正确舍入)。***
  • 中间结果总是舍入为53位。

您应该准备好处理这些问题,并假设大多数数学上等价的浮点表达式将不会产生相同的值。在 Python 中,您可以使用 math.isclose() 来估计两个浮点数是否“足够接近”,以便“可能是相同的值”。


* 实际上,这是一个谎言。 Python 遵循 C 的 double,通常以某种方式遵循 IEEE 754,但在足够奇特的架构上可能会偏离它。在这种情况下,C 标准提供了很少或没有保证,因此您将不得不查看您的架构或编译器的浮点文档。

** 假设指数不会溢出或下溢。如果发生这种情况,则通常会落在适当签名的无穷大或零上,或者根据架构和/或 Python 编译方式下溢到一个denormal number

*** “更复杂”的操作的确切集合有所不同,因为 IEEE 754 使许多操作变成可选项,同时要求精度。结果,很少明显是否给定操作符符合 IEEE 754 还是仅符合声名疏于 C 标准的规范。在某些情况下,操作可能根本不符合任何标准。


{btsdaf} - Jake

3

请注意,只要整数xy在Python浮点数中精确表示,x / y就是无限精确商的正确舍入值,在所有当前的机器上都是如此。这是IEEE 754浮点标准所要求的,并且所有当前的机器都支持。

因此,在您具体的示例中,重要的部分不是b = 4/6中的分子和分母有一个(特定!)公因数2,而是(a)它们有一些共同的因数; 和(b) 4和6都可以精确地表示为Python浮点数。

因此,例如,保证

(2 * 9892837) / (3 * 9892837) == 2 / 3

同样适用于这种情况。因为(2 * 9892837) / (3 * 9892837)的无限精确值与2/3的无限精确值相同,而IEEE 754除法会像计算出无限精确商一样进行计算。只要这些乘积在Python浮点数中保持完全可表示,就可以将9892837替换为任何其他非零整数。


2和2.0与这个例子的运行原理没有任何关联。 (n*x)/(n*y) == x/y 对于任意使得n*y不为0的整数而言都是成立的,并且n*xn*y都可以精确表示为Python浮点数。 - Tim Peters
{btsdaf} - Kevin

0

2/3 等同于 4/6。 (2/3)*(2/2) = 2/2 = 1,这是恒等元素。回答正确。


{btsdaf} - rayryeng

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接