Cannot calculate factorials bigger than 20! ! How to do so?

99封情书 提交于 2019-11-27 14:05:15
aring

The limit on an unsigned long long is 18446744073709551615, or about 1.8e+19. 20! is about 2.4e+18, so within range, however 21! is about 5.1e+19, exceeding the maximum size of an unsigned long long.

You may find this helpful: Are there types bigger than long long int in C++?

You're overflowing your integer type. It's likely that unsigned long long is 64 bits long for you.

  • 20! is 0x21c3_677c_82b4_0000 which fits.
  • 21! is 0x2_c507_7d36_b8c4_0000 which does not fit.

You can look into libraries like GMP which enable arbitrarily large integers.


To extend the GMP comment. Here's some code that will calculate the factorial using GMP:

void factorial(unsigned long long n, mpz_t result) {
    mpz_set_ui(result, 1);

    while (n > 1) {
        mpz_mul_ui(result, result, n);
        n = n-1;
    }
}

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

    factorial(100, fact);

    char *as_str = mpz_get_str(NULL, 16, fact);
    printf("%s\n", as_str);

    mpz_clear(fact);
    free(as_str);
}

This will calculate factorial(100), and results in:

0x1b30964ec395dc24069528d54bbda40d16e966ef9a70eb21b5b2943a321cdf10391745570cca9420c6ecb3b72ed2ee8b02ea2735c61a000000000000000000000000

And just for fun, here's the C++ version. Constructors, destructors and operator overloading tend to make the C++ version of these things look a bit cleaner. The result is the same as before.

#include <gmpxx.h>
#include <iostream>

int main() {
    mpz_class fact = 1;

    for (int i=2; i<=100; ++i)
        fact *= i;

    std::cout << "0x" << fact.get_str(16) << "\n";
}

Range of unsigned long long is usually 0 to 2^64 - 1 (18,446,744,073,709,551,615). 21! goes out of this range.

Indeed:

2^64 = 18446744073709551616
21!  = 51090942171709440000
20!  =  2432902008176640000

By the way, to calculate the result of a series (e.g., Taylor) you should not calculate each term separately; this will definitely bring you problems such as this one. Instead, try to calculate each term by reusing the previous one.

For example, the Taylor series for cos requires the summation of terms like:

(-1)^i * (x^(2*i)) / (2i)!

It is easy to see that each term can be calculated easily from the previous one:

newterm = - oldterm * x^2 / ((2i+1)*(2i+2))

So, I believe that you don't need to calculate large factorials, for what you're trying to do. On the other hand, if you need to, you'll have to use a library for big numbers, such as gmp.

factorial(25) should give the result 18,446,744,073,709,551,615 which is larger than the range of unsigned long long Data Type Ranges

A long long is only so big, and thus can only represent numbers so big. If you need an exact representation of bigger integers, you'll need to use something else (some 3-rd party library or some datatype you make yourself); if you don't need it to be exact, then you could use double's instead.

ConkerJoey

A simple algorithm that i wrote. but it's in Java.. You can calculate factorial of 1000 in about 15 minutes.

This algorithm works with the basic formula that we learnt at primary school.

/* FOR BEST RESULT DON'T CHANGE THE CODE UNTIL YOU KNOW WHAT YOU'RE DOING */
    public String factorial(int number){
        if(number == 0) return "1";
        String result = "1";
        for(int i = 0; i < number; i++){
            result = *longNumberMultiplyingAlgorithm*(result, "" + (i + 1));
        }
        return result;  
    }

    public String longNumberMultiplyingAlgorithm(String number1, String number2){
            int maxLength = Math.max(number1.length(), number2.length());
            int a = 0;
            String[] numbers = new String[maxLength];

            if(number2.length() > number1.length()){
                String t = number1;
                number1 = number2;
                number2 = t;
            }       

            for(int i = 0; i < number1.length(); i++){
                numbers[i] = "";
                a = 0;
                for(int j = 0; j < number2.length(); j++){
                    int result = Integer.parseInt(String.valueOf(number1.charAt(number1.length() - i - 1))) * Integer.parseInt(String.valueOf(number2.charAt(number2.length() - j - 1)));
                    if(result + a < 10){
                        numbers[i] = (result + a) + "" + numbers[i];
                        a = 0;
                    }else{
                        result += a;
                        a = (int)((result + 0.0) / 10);
                        result -= a * 10;
                        numbers[i] = result + "" + numbers[i];
                    }
                }
                if(a != 0){
                    numbers[i] = a + "" + numbers[i];
                }
                for(int k = 0; k < i; k++){
                    numbers[i] += "0";
                }
            }
            return longNumberAdditionAlgorithm(numbers);
        }

    private String longNumberAdditionAlgorithm(String[] numbers) {
            String final_number = "0";
            for(int l = 0; l < numbers.length; l++){
                int maxLength = Math.max(final_number.length(), numbers[l].length());
                String number = "";
                int[] n = new int[maxLength];
                int a = 0;
                for(int i = 0; i < n.length; i++){
                    int result = 0;
                    if(i >= final_number.length()){
                        result = Integer.parseInt(String.valueOf(numbers[l].charAt(numbers[l].length() - i - 1)));
                    }else
                    if(i >= numbers[l].length()){
                        result = Integer.parseInt(String.valueOf(final_number.charAt(final_number.length() - i - 1)));
                    }else{
                        result = Integer.parseInt(String.valueOf(final_number.charAt(final_number.length() - i - 1))) + Integer.parseInt(String.valueOf(numbers[l].charAt(numbers[l].length() - i - 1)));
                    }
                    if(result + a < 10){
                        number = (result + a) + "" + number;
                        a = 0;
                    }else{
                        result -= 10;
                        number = (result + a) + "" + number;
                        a = 1;
                    }
                }
                if(a == 1){
                    number = a + "" + number;
                }
                final_number = number;
            }
            return final_number;
        }
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!