4

Consider:

function fibo() {
    var first, second, add;
    for(var i=0; i<4000; i++) {
        if(i === 0) {
            first = 1;
            second = 2;
        }
        add = first + second;
        first = second;
        second = add;
    }
    alert(add);
}

fibo();

It is not working and shows infinity. Why?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
vetri02
  • 3,199
  • 8
  • 32
  • 43

9 Answers9

17

Simple: because it's too big.

The 300th term is 222232244629420445529739893461909967206666939096499764990979600, so you might imagine how big the 4000th is. You can't hold such value in a JavaScript variable.

If you really want to calculate it, use an arbitrary precision library, and maybe something other than JavaScript if you want to calculate it fast.

Check GNU Multiple Precision Arithmetic Library - GMP. Nice to use with C, and it even has special Fibonacci functions.


Here's a little C program to do the job:

#include <gmp.h>

int main()
{
    mpz_t num;
    mpz_init(num);

    mpz_fib_ui(num, 4000);
    gmp_printf("%Zd\n", num);

    return 0;
}

Compile with:

cc fib.c -lgmp

And run :-)

time ./a.out
39909473435004422792081248094960912600792570982820257852628876326523051818641373433549136769424132442293969306537520118273879628025443235370362250955435654171592897966790864814458223141914272590897468472180370639695334449662650312874735560926298246249404168309064214351044459077749425236777660809226095151852052781352975449482565838369809183771787439660825140502824343131911711296392457138867486593923544177893735428602238212249156564631452507658603400012003685322984838488962351492632577755354452904049241294565662519417235020049873873878602731379207893212335423484873469083054556329894167262818692599815209582517277965059068235543139459375028276851221435815957374273143824422909416395375178739268544368126894240979135322176080374780998010657710775625856041594078495411724236560242597759185543824798332467919613598667003025993715274875

real    0m0.005s
user    0m0.001s
sys     0m0.002s
sidyll
  • 57,726
  • 14
  • 108
  • 151
  • 1
    php's bcmath could handle an operation like that. Are you computing the value for something like Project Euler? – zzzzBov Nov 30 '11 at 19:16
  • 2
    `perl -M'Math::BigInt lib=>GMP' -E 'my $a = Math::BigInt->new(0); my $b = Math::BigInt->new(1); for (3..4000) { $c = $a + $b; $a = $b; $b = $c } say $c'` :-P – derobert Nov 30 '11 at 19:21
  • @weblearner check my updated answer. Such a stupid time to calculate it (sorry for the wording). – sidyll Nov 30 '11 at 19:29
  • @derobert I do love Perl, but check my C example and the time output :-) – sidyll Nov 30 '11 at 19:29
  • @sidyll: Yes, it's a fair bit faster—but of course it should be, as you're using the GMP Fibonacci functions, which have much better ways of computing the answer than iterating the sequence. Amazingly, the perl one runs in only 0.085s here. Assumably the perl one would be just as fast if it Math::GMP->fibonacci; but that'd lose the illustrative power of doing the calculation. (Testing it, it is; interesting, GMP counts term # differently than I did) – derobert Nov 30 '11 at 21:03
  • @derobert What a fantastic machine you have! Your program runs in 0.495s here (old Core2Duo, 2GHz, maybe the C program runs in 0.000s for you :-) yet it's really fast for such calculation. And sure, the internal functions are much faster. By the way, I didn't know about the formulas described in the functions page (the link I posted in the answer). – sidyll Nov 30 '11 at 22:40
  • @sidyll: This is a Core i7 930. Time for the C version shows 0.001s here. Which is faster than the perl `Math::GMP::fibonacci` one by .12 seconds, which is entirely the time it takes perl to load that module. (To be fair, it took 0.034s for `gcc -lgmp test.c -o test`, so perl loads the module faster than the C code compiles, unsurprisingly) – derobert Dec 01 '11 at 14:22
  • 1
    This answer should not be accepted. It has a false statement that JS can't deal with those large numbers. Several answers below present proper solutions to the problem. You can actually calculate larger numbers in a few ms in the SO snippet fcs –  Mar 10 '20 at 03:15
12

Just for the sake of completeness, here is a pure JavaScript solution. It uses a couple of libraries.

You will need biginteger and sloth. The solution also requires JavaScript 1.7 Generators.

var window = {};
load('biginteger.js');
load('sloth.js');
var sloth = window.sloth;


function fibonacci(){
  var fn1 = new BigInteger(1);
  var fn2 = new BigInteger(1);

  while (1){
    var current = fn2;
    fn2 = fn1;
    fn1 = fn1.add(current);
    yield current;
  }
}

var iter = sloth.iter(fibonacci());
var some = sloth.ify(iter).take(4000).force();
print(some[299]);
print(some[3999]);

The code is a little eccentric (the use of load() and window), because I wanted to test it in Rhino, without adding npm support. If that confuses you, just pretend you're seeing a require() call :)

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Mike Hogan
  • 9,933
  • 9
  • 41
  • 71
3

A simpler solution: Just store numbers in arrays, one digit per element, and perform addition like you did in elementary school - "in columns". It goes like this:

function add(a, b) {
    while (a.length < b.length) a.unshift(0);
    while (a.length > b.length) b.unshift(0);
    var carry = 0, sum = []
    for (var i = a.length - 1; i >= 0; i--) {
        var s = a[i] + b[i] + carry;
        if (s >= 10) {
            s = s - 10;
            carry = 1;
        } else {
            carry = 0;
        }
        sum.unshift(s);
    }
    if (carry)
        sum.unshift(carry);
    return sum;
}

And the Fibonacci function is like this:

function fib(n) {
    var f1 = [0];
    var f2 = [1];

    while (n--) {
        var f3 = add(f1, f2)
        f1 = f2;
        f2 = f3;
    }
    return f1.join("");
}

It seems totally ineffective, but it only takes fractions of a second to get fib(4000) on a 2.3 GHz MacBook.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
georg
  • 211,518
  • 52
  • 313
  • 390
2

Because Number.MAX_VALUE + Number.MAX_VALUE === Infinity

The issue is that sum exceeds JavaScript's capabilities for storing numeric values.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
zzzzBov
  • 174,988
  • 54
  • 320
  • 367
2

Call

fibonacci(4000)

Result (836 digits)

39909473435004422792081248094960912600792570982820257852628876326523051818641373433549136769424132442293969306537520118273879628025443235370362250955435654171592897966790864814458223141914272590897468472180370639695334449662650312874735560926298246249404168309064214351044459077749425236777660809226095151852052781352975449482565838369809183771787439660825140502824343131911711296392457138867486593923544177893735428602238212249156564631452507658603400012003685322984838488962351492632577755354452904049241294565662519417235020049873873878602731379207893212335423484873469083054556329894167262818692599815209582517277965059068235543139459375028276851221435815957374273143824422909416395375178739268544368126894240979135322176080374780998010657710775625856041594078495411724236560242597759185543824798332467919613598667003025993715274875

Code

<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>JavaScript Big Integers</title>

    <style>
        body{margin: 0 1em;width: auto;font-size: 125%}
        p{max-width: 800px;margin: 1ex 1em;}
        div{margin: 1em;}
        input, textarea, label{
            font-size: 1em;line-height: 1.2;font-family: arial, sans-serif;
            font-weight: 600;padding: 0 2px;
        }
        textarea{
            background-color: white;
            color: black;
            width: 90%;
            border: 2px ridge silver;
            position: absolute;
            z-index: 5;
            overflow-y: scroll;
            height: 500px;
        }
        #fibInp{text-align: right;}
        #calcfibBtn, #stopFibBtn{color:navy;cursor:pointer}
        #calcfibBtn:focus, #stopFibBtn:focus, #calcfibBtn:hover, #stopFibBtn:hover{color:red}
    </style>

    <script>
        // Utilities
        // Internet Explorer version (if any) determines the initial loop size
        /*@cc_on
            @if(@_jscript_version > 5.5){
                navigator.IEmod =
                  document.documentMode ? document.documentMode :
                  window.XMLHttpRequest ? 7 : 6;
            }
            @end
        @*/

        function mr(hoo){
            if(hoo){
                if(typeof hoo == 'string')
                    return document.getElementById(hoo);
                if(hoo.nodeType === 1)
                    return hoo;
            }
            return null;
        }
        if(!Array.prototype.map){
            Array.prototype.map = function(fun, scope){
                var L = this.length, A = Array(this.length), i = 0, val;
                if(typeof fun == 'function'){
                    while(i < L){
                        if(i in this){
                            A[i] = fun.call(scope, this[i], i, this);
                        }
                        ++i;
                    }
                    return A;
                }
            }
        }

        // Big Integer Object
        function BigInt(n, sign){
            if(this.constructor != BigInt){
                if(n.constructor == BigInt)
                    return n;
                n = n.toString();
                if(/^\d+$/.test(n)){
                    var digits = n.split('').map(Number);
                    return new BigInt(digits.reverse(), sign);
                }
                else{
                    throw Error('base 10 integer input only');
                }
            }
            while(n.length && !n[n.length - 1]){
                --n.length;
            }
            this.digits = n;
            this.length = n.length;
            if(sign == -1)
                this.sign = sign;
        }

        BigInt.prototype.toString = function(){
            var s = this.digits.slice().reverse().join('');
            if(this.sign == -1)
                s = '-' + s;
            return s;
        }

        BigInt.prototype.plus = function(n){
            n = BigInt(n);
            var n1 = this.digits, n2 = n.digits,
            len1 = n1.length, len2 = n2.length,
            hoo = Array(Math.max(len1, len2) + 1),
            size = Math.min(len1, len2), temp = 0, dig;
            for(var i=0; i<size; i++){
                dig = n1[i] + n2[i] + temp;
                hoo[i] = dig%10;
                temp = (dig/10)|0;
            }
            if(len2 > len1){
                n1 = n2;
                len1 = len2;
            }
            for(var i= ize; temp && i<len1; i++){
                dig = n1[i] + temp;
                hoo[i] = dig%10;
                temp = (dig/10)|0;
            }
            if(temp)
                hoo[i] = temp;
            while(i < len1){
                hoo[i] = n1[i];
                ++i;
            }
            return new BigInt(hoo);
        }

        // Math.fibonacci methods
        Math.fibonacci = function(n){
            var n1 = 0, n2 = 1, t = 1, fib = [], i = 0;
            var limit = 9007199254740992;
            while(n1 < limit){
                fib[i++] = n1;
                if(i== n)
                    return fib;
                n2 = t;
                t = n1 + n2;
                n1 = n2;
            }
            if(n){
                t = fib.pop(), n1 = fib.pop(), i = fib.length;
                while(i < n){
                    fib[i++] = n1;
                    n2 = BigInt(t);
                    t = n2.plus(n1);
                    n1 = n2.toString();
                }
            }
            return fib;
        }

        Math.nthFibonacci = function(n, ret){
            var fibs = [0, 1], i = 78;
            while(n && --i){
                fibs[2] = fibs[0] + fibs[1];
                fibs.shift();
                n--;
            }
            if(n){
                fibs = [BigInt(fibs[0]), BigInt(fibs[1])];
                while(n--){
                    fibs[2] = fibs[0].plus(fibs[1]);
                    fibs.shift();
                }
            }
            return ret ? fibs : fibs[0];
        }

        // Demonstration code
        Fib={
            clear: function(){
                mr('yw_fib_tex').value = '';
                Fib.wait = false;
                mr('fibInp').value = '';
            },
            counter: 1,
            demo: function(){
                mr('calcfibBtn').onclick = Fib.getFib;
                mr('stopFibBtn').onclick = Fib.quitFib;
                mr('fibInp').onkeypress = Fib.keycheck;
                mr('fibInp').focus();
            },
            discomma: !!window.opera,
            getFib: function(){
                mr('yw_fib_tex').value = '';
                var d, c, n = mr('fibInp').value;
                n = parseInt(mr('fibInp').value);
                if(!n || n<2){
                    mr('fibInp').value = '';
                    mr('fibInp').focus();
                    return true;
                }
                if(n <= 1100){
                    d = Math.nthFibonacci(n).toString();
                    var fibs = ['', n, d.length, d];
                    Fib.report(fibs);
                    return true;
                }
                if(n > 10000){
                    d = Fib;
                    c = d.counter;
                    if(c > 2000){
                        Fib.reach(d.low, d.high, n, c, Fib.report);
                        return true;
                    }
                }
                d = Math.nthFibonacci(1000, 1);
                Fib.reach(d[0], d[1], n, 1000, Fib.report);
                return true;
            },
            high: 1,
            keycheck: function(e){
                e = e || window.event;
                var k = e.charCode || e.keyCode;
                if(k == 13)
                    Fib.getFib();
                return true;
            },
            low: 0,
            quitFib: function(){
                Fib.wait = true;
                mr('yw_fib_tex').focus();
            },
            reach: function(n1, n2, n, i, cb){
                var d, q, who, mf = Fib, F = Fib.reach;
                if(F.time === undefined){
                    F.top = n;
                    F.count = i+1;
                    F.time = new Date().getTime();
                    F.cb = cb;
                    F.tik = false;
                }
                q = n2.toString().length;
                who = mr('yw_fib_tex');
                if(who){
                    if(q < 2100)
                        who.value = n2;
                    else
                        who.value = q + ' digits...thinking...';
                }
                if(q > 20000)
                    q = q > 100000 ? 10 : 20;
                else
                    if(q > 5000)
                        q = q > 10000 ? 50 : 100;
                else
                    q = q > 1000 ? 200 : 1000;
                if(navigator.IEmod) q /= 4;
                q = Math.min(q, F.top-F.count);
                while(q > 0){
                    d = BigInt(n1).plus(n2);
                    F.count++;
                    n1 = n2;
                    n2 = d;
                    --q;
                }
                if(F.count >= F.top || Fib.wait){
                    var t = (new Date()-F.time)/1000;
                    d = d.toString();
                    var fibs = [t, F.count, d.length, d, n1];
                    F.time = undefined;
                    if(typeof F.cb == 'function')
                        return F.cb(fibs);
                }
                else{
                    setTimeout(function(){
                        F(n1, d)
                    },
                    0);
                }
            },
            report: function(fb){
                var mf = Fib, s, fibz, f1 = fb[1], t = fb[0] || '', fN = fb[3];
                if(t){
                    t += mf.time;
                    if(mf.wait)
                        Fib.time += t;
                    else
                        Fib.time = 0;
                    t = t.toFixed(3) + ' seconds to calculate ';
                }
                fibz = t + 'fibonacci(' + f1 + ') [' + fb[2] + ' digits]';
                if(fb[4] && fN > mf.counter){
                    mf.counter = f1;
                    mf.low = fb[4];
                    mf.high = fN;
                }
                fN = fN.toString();
                if(window.opera){
                    fN = fN.replace(/(\d{10})/g, '$1 ');
                }
                fibz = fibz + '\n\n' + fN;
                mr('yw_fib_tex').value = fibz;
                Fib.wait = false;
                mr('yw_fib_tex').focus();
                return true;
            },
            time: 0,
            wait: false
        }
        window.onload = function(){
            Fib.demo();
        }
    </script>
</head>

<body>
    <h2 id="yw_fib_head">Fibonacci numbers in javascript</h2>
    <p>
    This is a demonstration of Big Integer Math
    in Javascript, handling numbers of arbitrary
    precision.
    The time it takes to calculate a large
    Fibonacci number depends on your
    computer and browser.</p>
    <div>
        <label>fibonacci#<input size="5" id="fibInp" type="text" value="1000"></label>
        <input type="button" id="calcfibBtn" value="Calculate">
        <input type="button" id="stopFibBtn" value="Stop">
        <br>
        <textarea readonly="readonly" id="yw_fib_tex">
        </textarea>
    </div>
</body>

</html>
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
kennebec
  • 102,654
  • 32
  • 106
  • 127
1

It is just a light change in the function to get the maximum position possible to calculate by JavaScript.

Yes, the result will be different on each browser and architecture being used.

function fibo() {
    var first, second, add;
    for(var i=0; i<4000; i++) {
        if(i === 0) {
            first = 1;
            second = 2;
        }
        if(first + second > Number.MAX_VALUE) {
            console.debug(i, first, second);
            return;
        }
        add = first + second;
        first = second;
        second = add;
    }
    alert(add);
}
fibo();

The result is: 1473 8.077637632156222e+307 1.3069892237633987e+308

Where 1473 is the maximum Fibonacci position possible to calculate with JavaScript.

The BigInt object can be used to increase the maximum integer value allowed by JavaScript, not limiting it to the computer architecture.

Gabriel Gartz
  • 2,840
  • 22
  • 24
  • Just a heads up: The values wont be accurate due to floating point precision. – zzzzBov Nov 30 '11 at 19:30
  • The link seems to be broken: *"This Serverless Function has crashed."* – Peter Mortensen May 22 '22 at 10:19
  • The jsperf is gone. Unfortunately, that was a good way to test with multiple browsers, versions, and architectures, to get the different max values. Nowadays using BigInt will allow to get the 4000th position. – Gabriel Gartz May 23 '22 at 13:39
0

PubNub's Ariya Hidayat taught us this:

function fibo(n) {
    return Array.apply(0, Array(n)).reduce(function(x, y, z){return x.concat((z < 2) ? z : x[z-1] + x[z-2]); }, []);
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
0

You can use WebWorkers and BigInt to calculate huge values of the Fibonacci sequence:

var input = document.querySelector('input');
var output = document.querySelector('#output');
var fulloutput = document.querySelector('#fulloutput');
var time = document.querySelector('#time');
var start, end;
var worker;

var createWorker = function () {
  if (worker) worker.terminate();
  var workerContent = "self.onmessage = " + workerFunction.toString();
  var blob = new Blob([workerContent], {type: 'application/javascript'});
  worker = new Worker(URL.createObjectURL(blob));
  worker.onmessage = receive;
};

var workerFunction = function(event) {
  var total = BigInt(event.data);
  var fib = function(index) {
    var temp;
    var last = BigInt(0);
    var sum = BigInt(1);
    for (; index >= BigInt(2); index--) {
      if (index % BigInt(1000) === BigInt(0))
        self.postMessage({
          total: total,
          calculating: index
        });
      temp = last;
      last = sum;
      sum += temp;
    }
    return sum;
  };
  if (total > BigInt(0)) self.postMessage({done: fib(total)});
  else self.postMessage({error: 'Input error'});
};

window.addEventListener('load', function () {
  if (!window.Worker ||
      !window.Blob   ||
      !window.URL    ||
      !window.BigInt) {
    output.textContent = 'Browser not supported';
  } else {
    start = performance.now();
    createWorker();
    output.textContent = 'Worker created, starting calculations';
    worker.postMessage(input.value);
    input.addEventListener('change', function () {
      createWorker();
      start = performance.now();
      output.textContent = 'Calculating';
      worker.postMessage(input.value);
    });
  }
});

var receive = function(event) {
  if (event.data.error) {
    output.textContent =  event.data.error;
  }
  if (event.data.calculating) {
    var total = BigInt(event.data.total);
    var index = BigInt(event.data.calculating);
    var progress = (BigInt(100) * (total - index) / total);
    output.textContent = 'Calculating ' + progress + '%';
  }
  if (event.data.done) {
    var formatter = new Intl.NumberFormat('en', {
      notation: 'scientific'
    });
    output.textContent = formatter.format(event.data.done);
    fulloutput.textContent = event.data.done;
    end = performance.now();
    time.textContent = 'It took ' + ((end - start) / 1000) + ' seconds';
  }
};
body {
  display: grid;
  place-content: center;
  min-height: 100vh;
}
p, pre  {
  text-align: center;
  width: 80vw;
  white-space:normal;
  word-wrap: break-word;
}
<p>N: <input type="number" value="4000"></p>
<pre id="output"></pre>
<pre id="fulloutput"></pre>
<pre id="time"></pre>
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
rafaelcastrocouto
  • 11,781
  • 3
  • 38
  • 63
0

there is actually 1475 numbers before infinite. this number is 1.3069892237633987e+308.

MCKJON
  • 1
  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – jasie Sep 07 '22 at 10:24
  • I do not see how this answers the question at the top of this page, but it should. Please [edit] according to [answer] or delete the answer. Otherwise it risks being flagged as "not an answer" and being deleted. – Yunnosch Sep 29 '22 at 16:21