奇怪的模运算符(%)结果

8
这里发生了什么事情?
>>> a = np.int8(1)
>>> a%2
1
>>> a = np.uint8(1)
>>> a%2
1
>>> a = np.int32(1)
>>> a%2
1
>>> a = np.uint32(1)
>>> a%2
1
>>> a = np.int64(1)
>>> a%2
1
>>> a = np.uint64(1)
>>> a%2
'1.0'

我们突然得到了一个看起来是包含浮点数 1.0字符串!?
>>> a = np.uint64(1)
>>> type(a%2)
<type 'numpy.float64'>

尽管实际上只是一个浮点数,但背后的哲学是什么?

我理解numpy想要在类型和类型规则等方面比基本Python更加严格,以使效率更高,但在这种情况下,将一个非常意外的结果返回给用户(很可能破坏他们的程序)的缺点似乎远远超过了在走下这条泥泞的道路之前检查模数符号所需的轻微成本增加。

在使用uint64值的情况并不罕见。例如,如果您将图像加载到numpy int数组中然后对其进行求和,则会得到uint64。另一方面,几乎不可能对负数取模(除了为了看看会发生什么,我从未这样做过),因为通常对可以计数的事物进行取模,例如索引,而不同的语言/标准/库可以各自拥有其自己的结果想法。

所有这些结合在一起让我感到相当困惑。


2
无法在2.7或3.6中复制。 - Stephen Rauch
2
我不明白为什么会有 numpy.float64,但是却没有字符串,奇怪... (3.5-np=1.14.0 and 2.7-np=1.14.3) - Julien
1个回答

2
我们突然得到了一个看起来包含浮点数1.0的字符串!?
这仍然是float64 - 只是由于numpy 1.14.3中的一个错误,它看起来很奇怪,这个问题在1.15.0-dev中已经修复。
通常有两种将其转换为字符串的方法 - __repr__ (tp_repr)和__str__ (tp_str)。
事实证明,在Python 2中还有一种方法 - tp_print。只有在直接输出到控制台或解释器时才会调用它。
结果发现,我们在解释器中实现了错误。在测试套件中测试解释器行为非常棘手!

虽然最后证明它只是一个浮点数。

这在某种程度上是有意设计的 - 推断出2np.int64(2),并强制进行{int64,uint64} -> float64的转换(以避免截断)。关于这个问题有很多问题,但很难修复。

请问您能详细说明一下“强制转换{int64,uint64} -> float64(以避免截断)”的含义吗? - Julien
2
int64(-1) + uint64(0)应该返回什么?uint64(2**32 - 1) + int64(1)呢?这里没有一个好的类型可以返回。 - Eric
考虑到人们几乎从不使用负数进行模运算,我认为在这种情况下将2解释为无符号整数是值得付出一点努力的。 - Apollys supports Monica
@Apollys:我认为你所指的是uint64 <op> positive[int]有一个明显的选择-这是难以修复的部分。 - Eric
是的,不幸的是我没有足够的低级别理解numpy的实现来欣赏这一点(即为什么难以实现正整数强制转换)。谢谢你的信息。 - Apollys supports Monica

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