Python的multiply()和prod()返回错误结果

1 投票
2 回答
2082 浏览
提问于 2025-04-15 19:26

有人能解释一下这个问题吗?我在用Python 2.5。

考虑一下这个计算:1*3*5*7*9*11 ... *49。如果你在IPython的交互式控制台里输入这个,你会得到58435841445947272053455474390625L,这个结果是正确的。(为什么用奇数:这只是我最开始的做法)

使用Python的multiply.reduce()或者prod()函数,应该能得到相同的结果,针对相应的范围。确实可以,但到了一定程度就不对了。在这里,结果已经错了:

: k = range(1, 50, 2)
: multiply.reduce(k)
: -108792223

使用prod(k)也会得到-108792223作为结果。对于长度为12的相应范围(也就是k = range(1,24,2)),其他错误的结果也开始出现。

我不太明白为什么会这样。有人能帮忙解释一下吗?

2 个回答

2

CPU(中央处理器)并不能随便处理非常大的数字,它只能对特定范围内的数字进行操作,这些数字是用二进制表示的,也就是0和1的组合。

而在Python中,使用'*'这个符号来进行乘法时,可以很好地处理大整数。这是因为Python有自己的一套表示方法和特别的代码,超出了CPU或浮点运算单元(FPU)直接支持的乘法指令。

这在编程语言中其实是比较特别的。

在大多数其他编程语言中,数字通常是以固定的比特数组来表示的。比如在C语言或SQL中,你可以选择使用一个8位的整数,这样它可以表示从0到255,或者从-128到+127;你也可以选择使用一个16位的整数,它可以表示到2的16次方减1,也就是65535。当只能表示有限范围的数字时,如果你用乘法(*)或加法(+)等操作超出了这个范围,就可能会出现不好的结果,比如得到一个负数。你可能在使用某个外部库时遇到过这样的问题,这个库可能是用C语言写的,而不是Python。

6

这是因为 numpy.multiply.reduce() 会把范围列表转换成 numpy.int32 类型的数组,而在某个时刻,进行的计算超出了32位能存储的范围,导致溢出:

>>> type(numpy.multiply.reduce(range(1, 50, 2)))
<type 'numpy.int32'>

正如Mike Graham所说,你可以使用 dtype 参数来选择使用Python的整数,而不是默认的类型:

>>> res = numpy.multiply.reduce(range(1, 50, 2), dtype=object)
>>> res
58435841445947272053455474390625L
>>> type(res)
<type 'long'>

但是在这种情况下,用numpy来处理Python对象是没有意义的,最好的解决方案是KennyTM的建议:

>>> import functools, operator
>>> functools.reduce(operator.mul, range(1, 50, 2))
58435841445947272053455474390625L

撰写回答