我正在使用numpy进行一些矢量化计算。我正在调查我遇到的一个错误,并以此行结束:
(vertices[:,:,:,0]+vertices[:,:,:,1]*256)*4
预计结果将是100728
指数vertices[0,0,17]
,但是,我得到35192
。当我试图将它改为4.0
而不是4
时,我结束了100728
的正确值,从而修复了我的bug。
我想理解为什么浮点在这里特别重要,我使用的是python 3.7,它是乘法,甚至不是除法。
额外的信息:
vertices.shape=(203759, 12, 32, 3)
python==3.7
numpy==1.16.1
编辑1:
这里的问题是你使用太小的整数,并且数字溢出并包裹,因为numpy使用固定宽度整数而不是像python int
那样的无限精度。 Numpy将"promote"基于输入的结果类型,但它不会根据是否发生溢出来促进结果(它是在实际计算之前完成的。
在这种情况下,当你乘以:vertices[:,:,:,1]*256
(我将称之为A
)时,256不能保存在uint8
中,因此它转到下一个更高的类型:uint16
这允许乘法的结果在这种情况下保持正确的值,因为verticies
中任何元素的最大可能值是255,所以可能的最大值是255 * 256,这在16位uint中很合适。
然后你添加vertices[:,:,:,0] + A
(我将称之为B
)。如果A
的最大值是255 * 256,并且vertices[:,:,:,0]
的最大值是255(再次是uint8
的最大值),则两者中的最大值等于216-1(您可以容纳的最大值) 16位无符号int)。直到你进行最后一次乘法,这仍然很好。
当你到达B * 4
时,numpy必须再决定返回类型应该是什么。整数4很容易适合uint16
,因此numpy不会将更高的类型提升为uint32
或uint64
,因为它不会先发制人地避免溢出,如前所述。这导致任何大于216-1的乘法乘积作为模216返回。
如果您改为使用浮点数(4. or 4.0
),numpy将其视为“更高”的值类型,无法放入uint16
中,因此它将结果提升为浮点数,这可以容纳更高的数字而不会溢出。
如果你不想改变整个数组:verticies
到一个更大的dtype,你可以简单地取结果B
并在你乘以4之前转换它:B.astype(np.uint64) * 4
。这将允许您保持更大的值而不会溢出(尽管如果值大于4,它实际上不会消除问题)。