简体   繁体   中英

How to return BigInteger from Math.Pow

I have the following function:

private static BigInteger Factorial(int number)
{
     if(number < 2) return BigInteger.One;
     double sum = 0;
     for(int i = 2; i < number; i++)
         sum += Math.Log(i);
     return new BigInteger(Math.Exp(sum));
}

This works fine for small input numbers like 10 , however if I pass a bigger number like 50000 it crashes and throws an OverflowException .

I understand why this is happening, the result of Math.Exp(sum) is just too big for a double . But that's why I'm trying to use BigInteger after all, to avoid these type of exceptions.

The problem is that wrapping the result like new BigInteger(Math.Exp(sum)) is useless because Map.Exp(sum) is trying to return a double anyway.

So I decided to use BigInteger.Pow static function:

return BigInteger.Pow(new BigInteger(Math.E), number);

Note the new BigInteger(Math.E) part. BigInteger.Pow takes a BigInteger as the first parameter so I don't have a choice but to wrap my Math.E to a BigInteger .

However, by doing this I am actually truncating the decimal part of my Math.E which ruins my algorithm because I end up with a totally different result.

I was looking for something like BigDouble or something similar, but the only class that I seem to find is BigInteger .

How can I successfully return the correct result as a BigInteger on this function and at the same time have an exception-safe code when big input is received?

You could use base 2 instead of base e :

private static BigInteger FactorialEstimate(int number)
{
    if (number < 2) return BigInteger.One;
    double sum = 0;
    for (int i = 2; i <= number; i++)
        sum += Math.Log(i, 2);
    return BigInteger.Pow(2, (int)Math.Round(sum));
}

If you need a more accurate answer:

private static BigInteger NthRoot(BigInteger a, int n)
{
    BigInteger min = BigInteger.Zero, max = a, mid = a;
    while (min < max)
    {
        mid = (min + max) >> 1;
        if (BigInteger.Pow(mid, n) < a)
            min = mid + 1;
        else
            max = mid;
    }
    return max;
}

const int Accuracy1 = 16;
const int Accuracy2 = 8;
private static BigInteger FactorialBetterEstimate(int number)
{
    if (number < 2) return BigInteger.One;
    double sumFractPart = 0;
    int sumIntPart = 0, tmpIntPart = 0;
    for (int i = 2; i <= number; i++)
    {
        sumFractPart += Math.Log(i, 2);
        tmpIntPart = (int)sumFractPart;
        sumFractPart -= tmpIntPart;
        sumIntPart += tmpIntPart;
    }

    int correction = Math.Min(Accuracy2, sumIntPart);
    sumIntPart -= correction;
    sumFractPart += correction;

    return NthRoot(BigInteger.Pow(2, Convert.ToInt32(Accuracy1 * sumFractPart)), Accuracy1) << sumIntPart;
}

But you only gain considerable speedup at numbers like 100000 compared to the exact factorial function. And at that scale all the functions get really slow.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM