Reputation: 303
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?
Upvotes: 5
Views: 3905
Reputation: 61992
I'll provide another answer that is hopefully more clear. The point is: Since the precision of System.Double
is limited to approx. 15-17 decimal digits, the result of any Pow(BigInteger, Double)
calculation will have an even more limited precision. Therefore, there's no hope of doing better than carlosfigueira's answer does.
Let me illustrate this with an example. Suppose we wanted to calculate
Pow(10, exponent)
where in this example I choose for exponent
the double-precision number
const double exponent = 100.0 * Math.PI;
This is of course only an example. The value of exponent
, in decimal, can be given as one of
314.159265358979
314.15926535897933
314.1592653589793258106510620564222335815429687500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000...
The first of these numbers is what you normally see (15 digits). The second version is produced with exponent.ToString("R")
and contains 17 digits. Note that the precision of Double
is less than 17 digits. The third representation above is the theoretical "exact" value of exponent
. Note that this differs, of course, from the mathematical number 100π near the 17th digit.
To figure out what Pow(10, exponent)
ought to be, I simply did BigInteger.Log10(x)
on a lot of numbers x
to see how I could reproduce exponent
. So the results presented here simply reflect the .NET Framework's implementation of BigInteger.Log10
.
It turns out that any BigInteger x
from
0x0C3F859904635FC0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
through
0x0C3F85990481FE7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
makes Log10(x)
equal to exponent
to the precision of 15 digits. Similarly, any number from
0x0C3F8599047BDEC0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
through
0x0C3F8599047D667FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
satisfies Log10(x) == exponent
to the precision of Double
. Put in another way, any number from the latter range is equally "correct" as the result of Pow(10, exponent)
, simply because the precision of exponent
is so limited.
(Interlude: The bunches of 0
s and F
s reveal that .NET's implementation only considers the most significant bytes of x
. They don't care to do better, precisely because the Double
type has this limited precision.)
Now, the only reason to introduce third-party software, would be if you insist that exponent
is to be interpreted as the third of the decimal numbers given above. (It's really a miracle that the Double
type allowed you to specify exactly the number you wanted, huh?) In that case, the result of Pow(10, exponent)
would be an irrational (but algebraic) number with a tail of never-repeating decimals. It couldn't fit in an integer without rounding/truncating. PS! If we take the exponent to be the real number 100π, the result, mathematically, would be different: some transcendental number, I suspect.
Upvotes: 1
Reputation: 61992
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? :-)
Upvotes: 2
Reputation: 87273
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))
)
Upvotes: 6