添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
Collectives™ on Stack Overflow

Find centralized, trusted content and collaborate around the technologies you use most.

Learn more about Collectives

Teams

Q&A for work

Connect and share knowledge within a single location that is structured and easy to search.

Learn more about Teams

OverflowError 'Numerical result out of range' when generating fibonacci numbers [duplicate]

Ask Question

I can feed the fib function numbers up to 700, where it starts to

OverflowError: (34, 'Numerical result out of range')

Do I need to use a special type like long to get around this?

The problem is that you're using doubles to calculate the value and the doubles are overflowing. Doubles give exact solutions only to about the 85th Fibonacci number.

If you want a fast and accurate calculation you're better off using an algorithm based on a better recurrence relationship, and using python bignum integers.

In particular you can use:

 fib(2*n) = fib(n)^2 + fib(n-1)^2
 fib(2*n-1) = fib(n)*(2*fib(n-1)+fib(n))

Or the equivalent matrix exponentiation formula (excuse the ugly formatting)

 [ F_n     F_{n-1} ]      [ 1   1 ] ^N 
 [                 ]  =   [       ]
 [ F_{n-1} F_{n-2} ]      [ 1   0 ]

Both of these result in algorithms that require O(log(N)) calculations rather than O(N).

Here's a complete solution in pseudo-code

If you do want to perform your calculations using doubles and the explicit formulae, then the formulae can be tweaked to give something faster that doesn't overflow until about the 1500th fibonacci number, and remains the same accuracy as your version. IIRC it is:

def fib(n):                                                                                                            
    return round( ((1+math.sqrt(5))/2)**n / math.sqrt(5) )
                Should be round rather than floor. The suppressed term ((1 - sqrt(5))/2)**n has alternating sign.
– Daniel Fischer
                Oct 2, 2012 at 22:35
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
OverflowError: (34, 'Numerical result out of range')

This method won't work well as floating point numbers don't have enough precision

for example, here

>>> (1+math.sqrt(5))**600
1.024664165563927e+306

you are working with only the first 15 or so digits. The remaining 291 will be treated as zeros when you do any arithmetic

See wikipedia for more information about accuracy problems with floating point numbers

nicer to put fib(n, memo={0:0, 1:1}) in as a mutable default argument. memo will be created at function definition time, but remains in the local scope. – wim Oct 1, 2012 at 1:52 +1. But… print fib(1000) will raise a RuntimeError: maximum recursion depth exceeded (at least with 64-bit Mac CPython 2.7.2; it may vary). So this doesn't actually get him much farther than his original code. Except, of course, that you can get around that RuntimeError by just doing fib(500); print fib(1000). – abarnert Oct 1, 2012 at 4:21 What's not explicit about this use case? When the function in question does not quite deserve to be a class object, it's less ugly than using a global variable. And there are examples of this pattern in the standard libraries iirc – wim Oct 1, 2012 at 10:18 @thg435: Guido acknowledged that one of the warts in 3.x is that the "default variable hack" is still the Pythonic way of doing this, even though it shouldn't be. There is no better solution in the language, and attempts to modify the language to add a better solution all seemed to cause worse problems than they solved. – abarnert Oct 2, 2012 at 18:06

If you actually want to use that algorithm, and you want to work beyond the limits of the built-in float, then yes, you need a different type.

If all you want is to get an approximate answer instead of an exception, that's easy; you can get infinite range out of the box. But if you also want to eliminate the rounding errors, you can't have infinite precision (that would take infinite time/space), so you have to know how to work out the precision needed for your range of inputs. (I'll leave that as an exercise for the reader.)

The standard library type decimal.Decimal may be all you need. It provides arbitrary-precision fixed- or floating-point decimal arithmetic according to the IEEE-854 standard. There are many cases for which it's unusable because it doesn't provide enough mathematical functions, but you only need basic arithmetic and sqrt, which are just fine. It can also be slow for huge numbers, but if you just want to calculate fib on a few three-digit numbers it's more than sufficient.

When Decimal is insufficient, there are a number of third-party modules, usually wrapping industry-standard C libraries like gmp/mpfr, such as bigfloat.

Here's how to get infinite range, but with rounding errors on roughly the same scale as the built-in float:

>>> s5 = decimal.Decimal(5).sqrt()
>>> def fib(n):
...     return ((1+s5)**n - (1-s5)**n)/(2**n*s5)
>>> fib(800)
Decimal('6.928308186422471713629008226E+166')
>>> int(fib(800))
69283081864224717136290082260000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000L
>>> s5 = bigfloat.sqrt(5)
>>> def fib(n):
...     return ((1+s5)**n - (1-s5)**n)/(2**n*s5)
>>> fib(800)
BigFloat.exact('6.9283081864226567e+166', precision=53)
>>> int(fib(800))
69283081864226566841137772774650010139572747244991592044952506898599601083170460360533811597710072779197410943266632999194601974766803264653830633103719677469311107072L

But notice that neither of these are actually the answer you'd get if you did the math perfectly; you've lost 24 digits to rounding errors. (The reason the values are different is that bigfloat is rounding in base 2, decimal in base 10.)

To fix that, you need more precision. All libraries provide some way to change the precision; bigfloat has more convenient options than most, but none are too onerous:

>>> decimal.getcontext().prec = 300
>>> s5 = decimal.Decimal(5).sqrt()
>>> def fib(n):
...     return ((1+s5)**n - (1-s5)**n)/(2**n*s5)
>>> fib(800)
69283081864224717136290077681328518273399124385204820718966040597691435587278383112277161967532530675374170857404743017623467220361778016172106855838975759985190398725.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000048
>>> def fibp(n, p):
...     with bigfloat.precision(p):
...         s5 = bigfloat.sqrt(5)
...         return ((1+s5)**n - (1-s5)**n)/(2**n*s5)
>>> fibp(800, 125)
BigFloat.exact('6.92830818642247171362900776814484912138e+166', precision=125)
>>> int(fibp(800, 125))
69283081864224717136290077681448491213794574774712670552070914552025662674717073354503451578576268674564384721027806323979200718479461097490537109958812524476157132800L
                Decimal doesn't propagate errors the same way as bigfloat (it makes more sense to computer engineers, less sense to scientists), and it's much slower, and doesn't provide nearly as many mathematical functions. Then again, it does provide sqrt, which is all that's needed for the OP, and if he doesn't already understand error propagation, he's not going to be disappointed by the IEEE model, so the only real issue is speed. I'll update the answer.
– abarnert
                Oct 3, 2012 at 18:25