20

In my C code, I want to calculate the factorial for numbers in the range 1 to 100. For small numbers, the function works, but for bigger numbers (for example 100!) it returns incorrect result. Is there any way to handle factorial of large numbers in C?

The compiler I'm using is gcc v4.3.3. My code is as follows:

#include <stdio.h>
#include <math.h>

double print_solution(int);

int main(void)
{
        int no_of_inputs, n;
        int ctr = 1;

        scanf("%d",&no_of_inputs); //Read no of inputs

        do
        {
                scanf("%d",&n); //Read the input
                printf("%.0f\n", print_solution(n));
                ctr++;  
        } while(ctr <= no_of_inputs);

        return 0;       
}

double print_solution(int n)
{
        if(n == 0 || n == 1)
                return 1;
        else
                return n*print_solution(n-1);
}
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278

16 Answers16

40

No standard C data type will accurately handle numbers as large as 100!. Your only option if to use arbitrary precision integer arithmetic, either through a library or done by yourself.

If this is just some hobby project, I'd suggest trying it yourself. It's kind of a fun exercise. If this is work-related, use a pre-existing library.

The largest C data type you'll normally get is a 64-bit integer. 100! is in the order of 10157, which takes at least 525 bits to store accurately as an integer.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
cletus
  • 616,129
  • 168
  • 910
  • 942
  • 6
    +1 on the fun part. We did this in college. It was illustrative at the very least. If you are doing this for professional purposes, use an existing library. – D.Shawley Sep 05 '09 at 21:52
  • how would you go about implementing arbitrary precision integer arithmetic in C without using a pre-existing library? – AlexBrand Jul 27 '10 at 03:27
  • 3
    @alexBrand store numbers are arrays. This could be arrays of digits (characters), packing two digits into a single byte (high and low 4 bits using 0-9 for each) or one of many other schemes. Arrays can be as large as memory allows. You then need to implement the arithmetic operations in terms of those data structures. – cletus Jul 27 '10 at 03:56
18

100 factorial is huge, to be precise it's 93326215443944152681699238856266700490715968264381621468592963895217 59999322991560894146397615651828625369792082722375825118521091686400 00000000000000000000.

Maybe you should use a bignum library like GMP. It's got nice docs, a pretty consistent interface, speed and if you're on Linux your distro probably has a package (I think mine installs it by default)

Stefano Borini
  • 138,652
  • 96
  • 297
  • 431
varzan
  • 587
  • 3
  • 18
15

To approximately compute factorials of large numbers you can go this way:

n! =  n * (n-1)!
so log(n!) = log(n) + log(n-1!)

Now you can use dynamic programming to compute log(n!) and calculate
n! as (base)^(log-value)

sud03r
  • 19,109
  • 16
  • 77
  • 96
9

If you don't want to use a bigint library, the best you can do with the stdlib is using long double and tgammal() from math.h:

long double fact(unsigned n)
{
    return tgammal(n + 1);
}

This'll get you 100! with a precision of 18 decimals on x86 (ie 80 bit long double).

An exact implementation isn't that complicated either:

#include <math.h>
#include <stdio.h>
#include <string.h>

void multd(char * s, size_t len, unsigned n)
{
    unsigned values[len];
    memset(values, 0, sizeof(unsigned) * len);
    for(size_t i = len; i--; )
    {
        unsigned x = values[i] + (s[i] - '0') * n;
        s[i] = '0' + x % 10;
        if(i) values[i - 1] += x / 10;
    }
}

void factd(char * s, size_t len, unsigned n)
{
    memset(s, '0', len - 1);
    s[len - 1] = '1';
    for(; n > 1; --n) multd(s, len, n);
}

int main(void)
{
    unsigned n = 100;
    size_t len = ceill(log10l(tgammal(n + 1)));
    char dstr[len + 1];
    dstr[len] = 0;
    factd(dstr, len, n);
    puts(dstr);
}
Christoph
  • 164,997
  • 36
  • 182
  • 240
  • That's an interesting definition of "not complicated" you're using there. To me, "not complicated" means something like `type_t fact(type_t x) { return x ? x * fact(x - 1) : 1; }` – Chris Lutz Sep 05 '09 at 21:56
  • I didn't say 'not complicated' ;) - it's just not *that complicated*: if you know how to multiply manually, the implementation is straight-forward... – Christoph Sep 05 '09 at 22:06
  • I like this solution but I'm confused about one thing: doesn't tgamma(n) = factorial(n-1). I.e. does this not compute the factorial first, to get the length of it as chars, then manually compute the factorial again? – Ross Apr 19 '15 at 04:03
  • @Ross: Indeed it does. It's not an essential part of the algorithm, though: you could have your poor-man's-bigint grow instead or use another approximation for `log_10(n!)`. For the given toy problem (factorials from 1 to 100), this was just the most convenient way that made it work – Christoph Apr 19 '15 at 10:10
5

Everyone is telling you the correct answer however a couple of further points.

  1. Your initial idea to use a double to get a wider range is not working because a double can not store this data precisely. It can do the calculations but with a lot of rounding. This is why bigint libraries exist.

  2. I know this is probably an example from a tutorial or examples site but doing unbounded recursion will bite you at some point. You have a recursive solution for what is essentially an iterative process. You will understand why this site is named as it is when you try running your program with larger values (Try 10000).

A simple iterative approach is as follows

  int answer, idx;

  for (answer = 1, idx = 1; idx <= no_of_inputs; idx++ ) {
    answer = answer * idx;
  }
  printf("Factorial of %3d =  %d\n", no_of_inputs, answer);
Steve Weet
  • 28,126
  • 11
  • 70
  • 86
  • @Steve Weet - You could memoize the function, which would allow it to calculate progressively larger factorials. I've found in C that there's generally very little performance gain, but that can help alleviate the recursion problem. – Chris Lutz Sep 05 '09 at 22:03
  • @Chris: I think you are thinking of recursively calculating Fibonacci numbers---a recursive factorial function should still only make N (or N+1, depending on whether you base out at 1 or 0) calls to calculate fact(N). The concern is that you're generating N or N+1 stack frames. A solution is either to write it iteratively or utilize tail recursion (gcc at least will optimize tail calls with -O2). This requires adding an explicit accumulator argument. – Derrick Turk Feb 26 '10 at 16:18
  • You're going to break `double` long before you overflow the stack. – Ben Voigt Sep 11 '14 at 15:35
2

this is what i made to solve a google riddle some years ago, it uses GMP library http://gmplib.org/:

#include <stdio.h>
#include "gmp.h"

void fact(mpz_t r,int n){
    unsigned int i;
    mpz_t temp;
    mpz_init(temp);
    mpz_set_ui(r,1);
    for(i=1;i<=n;i++){
        mpz_set_ui(temp,i);
        mpz_mul(r,r,temp);
    }
    mpz_clear(temp);
}
int main(void) {
    mpz_t r;
    mpz_init(r);
    fact(r,188315);
    /* fact(r,100); */
    gmp_printf("%Zd\n",r);
    mpz_clear(r);
    return(0);
}

gcc -lgmp -o fact fact.c

./fact

1

This is most certainly due to overflow. You need a way to represent large numbers (unsigned long long won't even cover up to 21!).

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Michael Foukarakis
  • 39,737
  • 6
  • 87
  • 123
1

you could try going for "unsigned long long" type, but thats the maximum you can get with built in types. I'd suggest (as cletus has already mentioned) either going with a known implementation of big numbers, or writing one yourself. "its a nice exercise" x 2.

rmn
  • 2,386
  • 1
  • 14
  • 21
1

If you want to use only the standard data types and you do not need the exact answer, then compute the logarithm of n! instead of n! itself. The logarithm of n! fits easily in a double (unless n is huge).

Jitse Niesen
  • 4,492
  • 26
  • 23
1

Any ways to handle factorial of large numbers in C ?

As factorials can rapidly exceed the range of standard fixed width integers and even floating point types like double, Code should consider a user type that allows for unbounded integer precision for an exact answer.

Various wide integer precision libraries exist, yet if code needs a simple solution, consider using a string.

The below is not fast, nor mindful of arrays bounds, yet is illustrative of the idea. The converting of '0'-'9' to/from 0-9 so much is wasteful, yet this does allow easy step-by-step debugging.

#include <stdlib.h>
#include <string.h>
#include <stdio.h>

static char *strfact_mult(char *s, unsigned x) {
  unsigned sum = 0;
  size_t len = strlen(s);
  size_t i = len;
  while (i > 0) {
    sum += (s[--i] - '0') * x;
    s[i] = sum % 10 + '0';
    sum /= 10;
  }
  while (sum) {
    len++;
    memmove(&s[1], s, len);
    s[i] = sum % 10 + '0';
    sum /= 10;
  }
  return s;
}

char *str_fact(char *dest, unsigned n) {
  strcpy(dest, "1");
  while (n > 1) {
    strfact_mult(dest, n--);
  }
  return dest;
}

void test_fact(unsigned n) { 
  char s[1000];
  printf("%3u %s\n", n, str_fact(s, n));
}

int main(void) {
  test_fact(0);
  test_fact(4);
  test_fact(54);
  test_fact(100);
}

Output

  0 1
  4 24
 54 230843697339241380472092742683027581083278564571807941132288000000000000
100 93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
1

Here is a solution for your question:

#include <stdio.h>
void factorial(int b){
    int temp = 0, r, size = 0, x;
    int arr[200] = {0};
    int l_b = b-1;
    while(b>0){
        r = b%10;
        arr[size++] = r;
        b = b/10;
    }
    while(l_b >= 2){
        int i=0;
        while(size>0){
         x = arr[i]*l_b+temp ;
         arr[i++] = x%10;
         temp = x/10;
         size--;
        }
       while(temp>0){
          arr[i++] = temp%10;
          temp = temp/10;
       }
      size = i;  --l_b;
    }
    for(int k=size-1;k>=0;k--)
        printf("%d",arr[k]);//ok i'm taking space here
    printf("\n");
}
int main(void) {
    // your code goes here
    int fact;

    scanf("%d\n",&fact);
    factorial(fact); 

    return 0;
}
Kokul Jose
  • 1,384
  • 2
  • 14
  • 26
1

Factorials up to 12! fit into a 32-bit integer. Factorials up to 20! fit into a 64-bit integer. After that, you've run out of bits on most machines. However, 34! fits into an unsigned 128-bit integer, 57! fits into a 256-bit integer, and 98! fits into an unsigned 512-bit integer. To calculate 100! as an integer, you need at least 525 bits.

This bc script calculates factorials (up to 35! but you can change the limit easily):

#!/usr/bin/bc -l

define f(n) {
   auto r, i
   r = 1
   for (i = 1; i <= n; i++)
   {
       r *= i;
       print "n = ", i, ", log2 = ", l(r)/l(2), ", n! = ", r, "\n"
   }
}

f(35)
quit

And some sample values:

# Key values
# n =  1, log2 =   0.00000000000000000000, n! = 1
# n =  2, log2 =   1.00000000000000000000, n! = 2
# n =  3, log2 =   2.58496250072115618147, n! = 6
# n =  4, log2 =   4.58496250072115618149, n! = 24
# n =  5, log2 =   6.90689059560851852938, n! = 120
# n =  6, log2 =   9.49185309632967471087, n! = 720
# n =  7, log2 =  12.29920801838727881834, n! = 5040
# n =  8, log2 =  15.29920801838727881836, n! = 40320
# n =  9, log2 =  18.46913301982959118130, n! = 362880
# n = 10, log2 =  21.79106111471695352921, n! = 3628800
# n = 11, log2 =  25.25049273335425078544, n! = 39916800
# n = 12, log2 =  28.83545523407540696694, n! = 479001600
# n = 13, log2 =  32.53589495221649912738, n! = 6227020800
# n = 14, log2 =  36.34324987427410323486, n! = 87178291200
# n = 15, log2 =  40.25014046988262176421, n! = 1307674368000
# n = 16, log2 =  44.25014046988262176426, n! = 20922789888000
# n = 17, log2 =  48.33760331113296117256, n! = 355687428096000
# n = 18, log2 =  52.50752831257527353551, n! = 6402373705728000
# n = 19, log2 =  56.75545582601885902935, n! = 121645100408832000
# n = 20, log2 =  61.07738392090622137726, n! = 2432902008176640000
# n = 21, log2 =  65.46970134368498166621, n! = 51090942171709440000
# ...
# n = 34, log2 = 127.79512061296909618950, n! = 295232799039604140847618609643520000000
# n = 35, log2 = 132.92440362991406264487, n! = 10333147966386144929666651337523200000000
# ...
# n = 57, log2 = 254.48541573017643505939
# n = 58, log2 = 260.34339672530400718017
# ...
# n = 98, log2 = 511.49178048020535201128
# n = 99, log2 = 518.12113710028496163045
# n = 100, log2 = 524.76499329005968632625

For the factorials 57!, 58!, 98!, 99!, 100! I've omitted the factorial value as it spreads over multiple lines in the output and isn't all that important. Note that 100! requires at least 525 bits of precision.

This code is available in my SOQ (Stack Overflow Questions) repository on GitHub as file factorial.bc in the src/miscellany sub-directory.

You could use double or long double to extend the range of values, but you lose some accuracy.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
0

I guess that's because you are overflowing the int range, which is up to approx. 2 billions. You can get up to 4 billions if you use unsigned int, but beyond that you have to use the bigint library.

Stefano Borini
  • 138,652
  • 96
  • 297
  • 431
0

100! = 933262154439441526816992388562667004907159682643816214685929 6389521759999322991560894146397156518286253697920827223758251185210 916864000000000000000000000000

You can't represent a number this big with an int or a long.

gshauger
  • 747
  • 4
  • 16
0

In addition to the advice of others, I'd suggest getting familiar with the storage limits of the basic types (int, long, long long, ...) for whatever computer/platform you're actually using. ("When in doubt, print more out!")

One earlier poster referred to an 80-bit precision limit, but that's particular to an x86 CPU.

Another person cited ISO C90 several times, although C99 is the latest standard; even if many compilers haven't completely implemented C99, you will probably find that they very-very likely at least have support for long long, which should correspond to >= 64-bit precision.

Garen
  • 941
  • 2
  • 9
  • 20
  • Of course, in the decade since this was written, the C11 and C18 standards have been released. They don't materially affect the discussion, though — there are no relevant changes in these later standards. – Jonathan Leffler Feb 17 '20 at 04:08
-4

Don't use the recursive algorithm I think, it is super slow, even if it is cached it will be slow. This is just something you should consider.

The reason for this is when you call fact(100) you don't actually run it 100 times, you actually run that function 5050 times. Which is bad, if it is cached then it could be 100 times, however, it is still slower to run a function call with if statements then to run a loop.

double print_solution(int n)
{
    double rval = 1;
    unsigned int i;

    for( i = 1; i <= n; i++ ) {
        rval *= i;
    }

    return rval;

}

Using arbitary-precision arithmetic you could make it go very high, however, you need to use a library to do that, or you could make your own library, but that would take a lot of time to do.

jhuni
  • 425
  • 3
  • 4
  • 4
    Can you explain your 5050 calls? I can see that if you calculate each of the factorials between 1 and 100, then you'd call it that often, but for a single call to evaluate 100! - I am not yet convinced. – Jonathan Leffler Sep 06 '09 at 00:25
  • 2
    I think you are thinking of recursively calculating Fibonacci numbers---a recursive factorial function should still only make N (or N+1, depending on whether you base out at 1 or 0) calls to calculate fact(N). – Derrick Turk Feb 26 '10 at 16:09
  • The 5050 comes from (* 1/2 100 101) which is the same thing as summing the first 100 numbers. Now that I understand functional programming I realize that recursion is actually something that is good, so sorry for my previous statement. – jhuni Dec 06 '10 at 07:15