Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to get the BigInteger to the pow Double in C#?

I tried to use BigInteger.Pow method to calculate something like 10^12345.987654321 but this method only accept integer number as exponent like this:

BigInteger.Pow(BigInteger x, int y)

so how can I use double number as exponent in above method?

like image 253
Siyamak Shahpasand Avatar asked Feb 20 '23 01:02

Siyamak Shahpasand


2 Answers

There's no arbitrary precision large number support in C#, so this cannot be done directly. There are some alternatives (such as looking for a 3rd party library), or you can try something like the code below - if the base is small enough, like in your case.

public class StackOverflow_11179289
{
    public static void Test()
    {
        int @base = 10;
        double exp = 12345.123;
        int intExp = (int)Math.Floor(exp);
        double fracExp = exp - intExp;
        BigInteger temp = BigInteger.Pow(@base, intExp);
        double temp2 = Math.Pow(@base, fracExp);
        int fractionBitsForDouble = 52;
        for (int i = 0; i < fractionBitsForDouble; i++)
        {
            temp = BigInteger.Divide(temp, 2);
            temp2 *= 2;
        }

        BigInteger result = BigInteger.Multiply(temp, (BigInteger)temp2);

        Console.WriteLine(result);
    }
}

The idea is to use big integer math to compute the power of the integer part of the exponent, then use double (64-bit floating point) math to compute the power of the fraction part. Then, using the fact that

a ^ (int + frac) = a ^ int * a ^ frac

we can combine the two values into a single big integer. But simply converting the double value to a BigInteger would lose a lot of its precision, so we first "shift" the precision onto the bigInteger (using the loop above, and the fact that the double type uses 52 bits for the precision), then multiplying the result.

Notice that the result is an approximation, if you want a more precise number, you'll need a library that does arbitrary precision floating point math.

Update: If the base / exponent are small enough that the power would be in the range of double, we can simply do what Sebastian Piu suggested (new BigInteger(Math.Pow((double)@base, exp)))

like image 59
carlosfigueira Avatar answered Feb 27 '23 02:02

carlosfigueira


I like carlosfigueira's answer, but of course the result of his method can only be correct on the first (most significant) 15-17 digits, because a System.Double is used as a multiplier eventually.

It is interesting to note that there does exist a method BigInteger.Log that performs the "inverse" operation. So if you want to calculate Pow(7, 123456.78) you could, in theory, search all BigInteger numbers x to find one number such that BigInteger.Log(x, 7) is equal to 123456.78 or closer to 123456.78 than any other x of type BigInteger.

Of course the logarithm function is increasing, so your search can use some kind of "binary search" (bisection search). Our answer lies between Pow(7, 123456) and Pow(7, 123457) which can both be calculated exactly.

Skip the rest if you want

Now, how can we predict in advance if there are more than one integer whose logarithm is 123456.78, up to the precision of System.Double, or if there is in fact no integer whose logarithm hits that specific Double (the precise result of an ideal Pow function being an irrational number)? In our example, there will be very many integers giving the same Double 123456.78 because the factor m = Pow(7, epsilon) (where epsilon is the smallest positive number such that 123456.78 + epilon has a representation as a Double different from the representation of 123456.78 itself) is big enough that there will be very many integers between the true answer and the true answer multiplied by m.

Remember from calculus that the derivative of the mathemtical function x → Pow(7, x) is x → Log(7)*Pow(7, x), so the slope of the graph of the exponential function in question will be Log(7)*Pow(7, 123456.78). This number multiplied by the above epsilon is still much much greater than one, so there are many integers satisfying our need.

Actually, I think carlosfigueira's method will give a "correct" answer x in the sense that Log(x, 7) has the same representation as a Double as 123456.78 has. But has anyone tried it? :-)

like image 29
Jeppe Stig Nielsen Avatar answered Feb 27 '23 00:02

Jeppe Stig Nielsen