为什么 a//b 和 np.floor(a/b) 产生不同的结果?

Why do a//b and np.floor(a/b) produce different results?

提问人:YPOC 提问时间:7/26/2023 更新时间:7/27/2023 访问量:126

问:

我偶然发现了 python 3 的“地板分割”的一个奇怪的边缘行为,使用 .经过一番计算,我得出了公式。用//(a1 - a2) // b

a1 = 226.72560000000001
a2 = 0.2856000000000165
b = 1.02
# all types are np.float64

我得到的结果与我使用的结果不同:np.floor()

>>> (a1 - a2) // b
221.0
>>> np.floor((a1 - a2) / b)
222.0

这种差异显然源于浮点计算,但为什么它们的行为不同呢?使用更高精度的计算器,如 ke!san,我可以验证 的行为在数学上是正确的,而对于我的计算来说,将提供正确的结果。//np.floor()

python-3.x numpy 浮点 精度

评论

2赞 Nin17 7/26/2023
numpy.floor_divide 一致,则使用//numpy.floor(a1-a2)/b//1
1赞 ken 7/26/2023
你可以使用 divmod 而不是来查看它是如何解释的。此外,这发生在 math.floor 中,因此它与 numpy 无关。//
0赞 dan04 7/27/2023
相关问题。

答:

2赞 Mattravel 7/26/2023 #1

事实上,结果在数学上是正确的。//

但是,如果您只是这样做,您可以看到结果已经四舍五入到(由于浮动存储限制,它应该是),所以现在使用 floor 将返回 .(a1 - a2) / b222.0221.9999999999999936275222.0

编辑:实际上,首先a1-a2被舍入为(而不是),然后错误地返回而不是由于@Talha Tayyab解释(x//y返回(x-x%y)//y)。
因此,有两个连续的舍入误差,它们相互补偿并得出正确的结果。
//226.44226.4399999999999935226.44 // 1.02221222

-1赞 Harith 75 7/26/2023 #2

请记住,浮点运算可能很棘手,了解它的陷阱总是一个好主意。这不是 Python 或 NumPy 中的错误,而是浮点数的本质以及对它们起作用的算术运算。

您正在观察的行为是由于不同浮点排序之间的准确性差异造成的。

“漂移点”一词暗示了这些数字在内部的说话方式。微妙的元素非常专业,但关键的一点是,这些数字是使用固定数量的比特来说话的,你拥有的比特越多,你得到的精确度就越高。

Python 中(或 NumPy 等库)中的各种浮点数,例如 float16、float32 和 float64,指的是分别使用 16、32 和 64 位的浮点数。

float64(也称为“双精度漂移”)比 float32(“单精度漂移”)具有更高的精度和更大的运行量,而 float32 又比 float16(“半精度浮点数”)具有更高的精度和更大的范围。

因此,在使用不同的浮点排序执行相同的计算后,由于这些精确度的对比,您会得到一些不同的结果。这通常是数值计算中的常见问题,需要注意。

必须为您的特定使用案例选择正确的浮点类型。一般来说,float64 可能是一个很好的默认选择,因为它在准确性和内存利用率之间提供了很好的调整。但是,如果您正在处理异常广泛的数据集并且内存利用率可能是一个问题,或者如果您在支持 float32 或 float16 计算的 GPU 上执行计算,那么使用较低精度的漂移排序可能是有意义的。

这里有一个小代码告诉你为什么

这里有一个小代码告诉你为什么:

import numpy as np

a1 = np.float16(226.72560000000001)
a2 = np.float16(0.2856000000000165)
b = np.float16(1.02)

a3= float(226.72560000000001)
a4 = float(0.2856000000000165)
b1 = float(1.02)

print((a3 - a4) // b1)
print((a1 - a2) // b)

print(np.floor((a3 - a4) / b1))
print(np.floor((a1 - a2) / b))

输出:

221.0
222.0
222.0
222.0

您可以更改浮点数的类型,以查看它如何影响结果。

评论

0赞 anatolyg 7/26/2023
有人在你的文本上做了一堆随机替换,比如“float”→“coast”等,而你却没有注意?它仍然是可以理解的,但更难,而且看起来很有趣。
0赞 Harith 75 7/26/2023
如果您使用 np.float64,则输出将为 221.0、221.0、222.0、222.0
0赞 Nin17 7/26/2023
好的,但这并没有真正回答这个问题,只是说什么时候不会改变精度,在他们的情况下,他们对两者都使用相同的精度(a1-a2)//bnp.floor((a1-a2)/b)
0赞 Harith 75 7/26/2023
@anatolyg对不起,英语不是我的舌头,编辑谢谢。
0赞 Harith 75 7/26/2023
@Nin17嗯,好吧,我明白了。好吧,在这种情况下,Talha Tayyab 的答案是最好的
1赞 Goku - stands with Palestine 7/26/2023 #3

如果 x 非常接近 y 的精确整数倍,则由于四舍五入,x//y 可能大于 (x-x%y)//y。在这种情况下,Python 返回后一个结果,以保持 divmod(x,y)[0] * y + x % y 非常接近 x

文档链接:

https://docs.python.org/3/reference/expressions.html#id10

应该输出什么:

3.3//1.1

3.0 right?

wrong it is 2.0

因此,当分子非常接近分母的倍数时

x//y将返回:(x-x%y)//y

a1 = 226.72560000000001
a2 = 0.2856000000000165
b = 1.02

x = a1-a2
y = 1.02

(x-x%y)//y

#output
221.0