4

I am really confused. I am trying to calculate Fibonacci numbers, but as they get larger and large the numbers start to become wrong. and I do not know why.

How do you calculate accurate Fibonacci numbers using Binet's Formula, it is my understanding that this should always return a integer?

Here is what I have been trying to work with.

http://ideone.com/e6t6h

See as the number goes up. it gets all weird?

here I print it out with a cout.precision(15);

http://ideone.com/XREh2

here I print it out with cout << fixed << blah blah;

Here I have used a procedural loop to calculate it by going though the iterations.

This one is more accurate, than the one using Binet's formula.

Anyway. dose anyone have any code I can look at that can compute F(n) with out the need to iterate though every level of (n) by using Binet's formula?

Peter O.
  • 32,158
  • 14
  • 82
  • 96
aJynks
  • 677
  • 2
  • 14
  • 27
  • These were the two links i could not post in the original http://ideone.com/wfDyU http://ideone.com/koFkg – aJynks Mar 10 '12 at 09:03
  • 1
    Please try to show the relevant information *here* (for example, which results do you get, and what were the correct ones), and if possible, a *small* code sample that reproduces the problem. Having to hunt through three different ideone links to even figure out what you're asking does not encourage answers. :) And if you post the information *here*, then you don't run into problems with the spam protection either! – jalf Mar 10 '12 at 09:29
  • 2
    You need to learn [how to use floating point](http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html). You are using a hammer to bash in a screw and it's not surprising it gets bent. – David Schwartz Mar 10 '12 at 09:41
  • Binet's formula only holds for real numbers, not for floating point approximations of such. – molbdnilo Mar 10 '12 at 14:16

3 Answers3

16

To accurately calculate Fibonacci numbers using Binet's formula, you need an accurate interpretation of √5. Since √5 is irrational, it cannot be accurately represented using double or float, hence Binet's formula doesn't work with these types (however, the rounding in the computation leads to exact results for some small inputs). Since the Fibonacci numbers are integers, you can get exact results from Binet's formula using double or float for more arguments by rounding afterwards,

double binet(unsigned int n)
{
    static const double phi = (1 + sqrt(5))*0.5;
    double fib = (pow(phi,n) - pow(1-phi,n))/sqrt(5);
    return round(fib);
}

That will return the correct result for almost all n small enough that the result can be exactly represented as a double. These aren't many, however. A double typically has only 53 bits of precision, so only Fibonacci numbers smaller than 253 can be exactly represented as a double (plus a few larger ones divisible by sufficiently high powers of 2). The last Fibonacci number smaller than 253 is F(77), but F(78) is divisible by 8, so also exactly representable as a double with 53 bits of precision. However, the above produces correct results only for n <= 70 here, from 71 on, the rounding error is too large (incidentally, the result of Binet's formula using doubles is always too large here, so using floor instead of round would produce the correct result also for F(71), but no further).

With the standard datatypes, not many Fibonacci numbers are exactly representable, the last to fit in an (unsigned) 64 bit type is F(93); for 128 bits, the last is F(186). For so small indices, there is practically nothing to be gained over the straightforward iterative algorithm

unsigned long long fibonacci(unsigned int n)
{
    unsigned long long a = 0, b = 1;
    for(; n > 0; --n)
    {
        b += a;
        a = b-a;
    }
    return a;
}

unless you use a lookup table

static const unsigned long long fibs[94] = { 0, 1, 1, 2, ... , 12200160415121876738ull };

For accurate results, one must treat √5 (and/or φ) as a symbolic constant and evaluate the formula using that. This amounts to evaluating the formula in the ring

ℤ[φ] = { a + b*φ : a, b ∈ ℤ }

of algebraic integers in ℚ(√5), using the fact that φ² = 1 + φ. Equivalent to Binet's formula is

φ^n = F(n-1) + φ*F(n)

which can be used to efficiently calculate Fibonacci numbers by repeated squaring in O(log n) steps (but note that F(n) has Θ(n) bits, so the number of bit operations can't be lower than O(n)). A slightly more efficient version than the vanilla repeated squaring uses

φ^(2n) = (φ^n)² = (F(n-1) + φ*F(n))² = F(n-1)² + φ*2*F(n-1)*F(n) + φ²*F(n)²
       = (F(n-1)² + F(n)²) + φ*(2*F(n-1)*F(n) + F(n)²)

finding F(2n) = 2*F(n)*F(n-1) + F(n)² = 2*F(n)*F(n+1) - F(n)² = F(n)*(F(n+1) + F(n-1)) and F(2n+1) = F(n)² + F(n+1)², using φ² = 1 + φ. These formulae allow calculating F(2n), F(2n+1) and F(2n+2) from F(n) and F(n+1) with at most two multiplications and two additions/subtractions per number, which gives an algorithm to calculate the pair (F(n),F(n+1)) in O(log n) steps with only two numbers as state (vanilla repeated squaring uses four numbers as state and needs a few more multiplications).

An iterative left-to-right algorithm is

unsigned long long fib(unsigned int n){
    if (n == 0) return 0;
    unsigned int h = n/2, mask = 1;
    // find highest set bit in n, can be done better
    while(mask <= h) mask <<= 1;
    mask >>= 1;
    unsigned long long a = 1, b = 1, c; // a = F(k), b = F(k+1), k = 1 initially
    while(mask)
    {
        c = a*a+b*b;        // F(2k+1)
        if (n&mask)
        {
            b = b*(b+2*a);  // F(2k+2)
            a = c;          // F(2k+1)
        } else {
            a = a*(2*b-a);  // F(2k)
            b = c;          // F(2k+1)
        }
        mask >>= 1;
    }
    return a;
}

With an arbitrary precision type instead of unsigned long long, that allows the fast calculation of large Fibonacci numbers. But of course arbitrary precision libraries often come with their own optimised Fibonacci functions, so it's kind of moot to implement it yourself.

Daniel Fischer
  • 181,706
  • 17
  • 308
  • 431
  • Wow, David.. fantastic answer.. and more importantly you somehow decoded exactly what I was asking... ... you used round(fib); is that a function you have made called round that uses a if statement and ciel() / floor() to do the rounding? – aJynks Mar 10 '12 at 21:40
  • `round` is a standard library function, in C from `math.h`, in C++ from `cmath` or `math.h`, as one prefers. It rounds a `double` to the closest `double` with an integral value (I'm not sure, I think if you `#include ` you get an overloaded `round` that does the same for `float`, in C, you'd use `roundf` for that). – Daniel Fischer Mar 10 '12 at 22:09
2

In general, floats and doubles are not designed to represent numbers accurately. Their purpose is to represent real numbers in a wide range. If you do want infinite precision, you could try looking into http://gmplib.org/

Karolis Juodelė
  • 3,708
  • 1
  • 19
  • 32
  • AFAIK the GMP library is only for finite precision. You need something like PARI to use numbers like sqrt(5) in their exact representation. – Gunther Piez Mar 10 '12 at 10:00
  • So, I should just be using the recursive method of calculating though all iterations until I hit the desired "n" and use a unsigned int to store it... and this will be accurate? As this other method, due to the irrational numbers will always go haywire unless I use an external library? So what about the cout << fixed << and cout.precision stuff modifying what was going on? – aJynks Mar 10 '12 at 13:52
  • fixed and precision modify how it is printed. Really, in your code the numbers are small enough, so the errors are tiny. I think F(35) should start giving problems on float. Again, floats aren't exact, so displaying exact values is not friendly. Using recursive formula is much slower though. You could try casting the result to int (or long long int for numbers after F(47)) and then printing them. – Karolis Juodelė Mar 10 '12 at 15:07
1

Have you tried including <cmath> rather than <math.h>, math.h might not have an overloaded version of sqrt as it is for C

Chris Taylor
  • 52,623
  • 10
  • 78
  • 89
derekdreery
  • 3,860
  • 4
  • 29
  • 38