Alex
Alex

Reputation: 11

Wrong results with Python multiply() and prod()

Can anyone explain the following? I'm using Python 2.5

Consider 1*3*5*7*9*11 ... *49. If you type all that from within IPython(x,y) interactive console, you'll get 58435841445947272053455474390625L, which is correct. (why odd numbers: just the way I did it originally)

Python multiply.reduce() or prod() should yield the same result for the equivalent range. And it does, up to a certain point. Here, it is already wrong:

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

Using prod(k) will also generate -108792223 as the result. Other incorrect results start to appear for equivalent ranges of length 12 (that is, k = range(1,24,2)).

I'm not sure why. Can anyone help?

Upvotes: 1

Views: 2103

Answers (2)

Paul
Paul

Reputation: 27433

The CPU doesn't multiply arbitrarily large numbers, it only performs specific operations defined on particular ranges of numbers represented in base 2, 0-1 bits.

Python '*' handles large integers perfectly through a proper representation and special code beyond the CPU or FPU instructions for multiply.

This is actually unusual as languages go.

In most other languages, usually a number is represented as a fixed array of bits. For example in C or SQL you could choose to have an 8 bit integer that can represent 0 to 255, or -128 to +127 or you could choose to have a 16 bit integer that can represent up to 2^16-1 which is 65535. When there is only a range of numbers that can be represented, going past the limit with some operation like * or + can have an undesirable effect, like getting a negative number. You may have encountered such a problem when using the external library which is probably natively C and not python.

Upvotes: 2

Luper Rouch
Luper Rouch

Reputation: 9492

This is because numpy.multiply.reduce() converts the range list to an array of type numpy.int32, and the reduce operation overflows what can be stored in 32 bits at some point:

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

As Mike Graham says, you can use the dtype parameter to use Python integers instead of the default:

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

But using numpy to work with python objects is pointless in this case, the best solution is KennyTM's:

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

Upvotes: 6

Related Questions