Numpy 向量化 - 奇怪的问题
Numpy Vectorization - Weird issue
我正在使用 numpy 执行一些矢量化计算。我正在调查我遇到的错误,并以这一行结束:
(vertices[:,:,:,0]+vertices[:,:,:,1]*256)*4
索引 vertices[0,0,17]
的预期结果是 100728
,但是,我得到的是 35192
。
当我尝试将其更改为 4.0
而不是 4
时,我最终获得了 100728
的正确值,从而修复了我的错误。
我想了解为什么浮点数在这里很重要,尤其是我使用的是 python 3.7,它是乘法,甚至不是除法。
额外信息:
vertices.shape=(203759, 12, 32, 3)
python==3.7
numpy==1.16.1
编辑 1:
- 顶点类型是 "numpy.uint8"
- 顶点[0, 0, 17] => [94, 98, 63]
这里的问题是您使用的整数太小,数字会溢出并环绕,因为 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(又是a的最大值uint8
),则两者的最大和等于216-1(您可以在 16 位无符号整数中保存的最大值)。在你进行最后一次乘法之前,这仍然很好。
当你到达 B * 4
时,numpy 必须再次决定 return 类型应该是什么。整数 4 很容易适合 uint16
,因此 numpy 不会将类型提升到 uint32
或 uint64
,因为它不会像前面描述的那样抢先避免溢出。这导致任何大于 216-1 的乘积被 returned 为模 216.
如果您改为使用浮点数 (4. or 4.0
),numpy 会将其视为 "higher" 值类型,无法放入 uint16
,因此它会将结果提升为浮点数,可以容纳更大的数字而不会溢出。
如果您不想将整个数组 verticies
更改为更大的 dtype,您可以简单地获取结果 B
并在乘以 4 之前转换它:B.astype(np.uint64) * 4
。这将允许您持有 很多 更大的值而不会溢出(尽管如果值大于 4,它实际上并不能消除问题)。
我正在使用 numpy 执行一些矢量化计算。我正在调查我遇到的错误,并以这一行结束:
(vertices[:,:,:,0]+vertices[:,:,:,1]*256)*4
索引 vertices[0,0,17]
的预期结果是 100728
,但是,我得到的是 35192
。
当我尝试将其更改为 4.0
而不是 4
时,我最终获得了 100728
的正确值,从而修复了我的错误。
我想了解为什么浮点数在这里很重要,尤其是我使用的是 python 3.7,它是乘法,甚至不是除法。
额外信息:
vertices.shape=(203759, 12, 32, 3)
python==3.7
numpy==1.16.1
编辑 1:
- 顶点类型是 "numpy.uint8"
- 顶点[0, 0, 17] => [94, 98, 63]
这里的问题是您使用的整数太小,数字会溢出并环绕,因为 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(又是a的最大值uint8
),则两者的最大和等于216-1(您可以在 16 位无符号整数中保存的最大值)。在你进行最后一次乘法之前,这仍然很好。
当你到达 B * 4
时,numpy 必须再次决定 return 类型应该是什么。整数 4 很容易适合 uint16
,因此 numpy 不会将类型提升到 uint32
或 uint64
,因为它不会像前面描述的那样抢先避免溢出。这导致任何大于 216-1 的乘积被 returned 为模 216.
如果您改为使用浮点数 (4. or 4.0
),numpy 会将其视为 "higher" 值类型,无法放入 uint16
,因此它会将结果提升为浮点数,可以容纳更大的数字而不会溢出。
如果您不想将整个数组 verticies
更改为更大的 dtype,您可以简单地获取结果 B
并在乘以 4 之前转换它:B.astype(np.uint64) * 4
。这将允许您持有 很多 更大的值而不会溢出(尽管如果值大于 4,它实际上并不能消除问题)。