Search code examples
cfactorial

How to output integers more than 16 digits correctly in C?


The code here only gives correct output till the factorial of 21 and after that its correct upto 16 digits from the left then remaining digits are just given as zero. I tried changing the type of variable c from double to long double but it just gives errors or doesn't print the factorials.

#include <stdio.h>

FILE *fp;
long double facto(int i);
int main() {
    int n, i;
    double c;
    printf("enter no. to find factorial till:\n");
    scanf("%d", &n);
    fp = fopen("output_of_factorial.txt", "w");
    fputs("Number  |\t Factorial\n\n", fp);

    for (i = 1; i <= n; i++) {
        c = facto(i);
        fprintf(fp, "%d\t|\t %.0Lf\n", i, c);
    }
    fclose(fp);
    return 0;
}

long double facto(int x) {
    if (x == 1)
        return 1;
    else
        return x * facto(x - 1);
}

Solution

  • Tye double only has 53 bits of precision, long double probably has 80 bits on your platform. Using floating point arithmetics will give you an approximate result that is correct for the most significant digits. Using integers give you the exact result, but only if it is smaller than the range of the type.

    You can use the type long long that is at least 64-bit wide, thus 19 digits, or for one more bit, type unsigned long long that allows for integers twice as large:

    LLONG_MAX = 9223372036854775807  //  > 9.22e19
    ULLONG_MAX = 18446744073709551615  // > 1.84e20
    

    Here is a modified version of the code:

    #include <stdio.h>
    
    unsigned long long facto(int i);
    
    int main(void) {
        int n, i;
        unsigned long long c;
        FILE *fp;
    
        printf("enter no. to find factorial till: ");
        if (scanf("%d", &n) == 1) {
            fp = fopen("output_of_factorial.txt", "w");
            if (fp != NULL) {
                fputs("Number |            Factorial\n\n", fp);
                for (i = 1; i <= n; i++) {
                    c = facto(i);
                    fprintf(fp, "%6d | %20llu\n", i, c);
                }
                fclose(fp);
            }
        }
        return 0;
    }
    
    unsigned long long facto(int x) {
        if (x <= 1)
            return 1;
        else
            return x * facto(x - 1);
    }
    

    It works all the way to 20:

    Number |            Factorial
    
         1 |                    1
         2 |                    2
         3 |                    6
         4 |                   24
         5 |                  120
         6 |                  720
         7 |                 5040
         8 |                40320
         9 |               362880
        10 |              3628800
        11 |             39916800
        12 |            479001600
        13 |           6227020800
        14 |          87178291200
        15 |        1307674368000
        16 |       20922789888000
        17 |      355687428096000
        18 |     6402373705728000
        19 |   121645100408832000
        20 |  2432902008176640000
    

    But fails for 21 and above because of arithmetic overflow.

    To go further, you could use 128-bit integers if they are available on your platform (uint128_t, __uint128 or __uint128_t) but you would need to write your own conversion function to output the decimal representation.

    A better approach would be to use multi-precision (aka bignum) packages that can handle extremely large numbers, typically only bound by available memory.