4

我尝试使用BigInteger.Pow方法来计算类似 10^12345.987654321 的东西,但这种方法只接受整数作为指数,如下所示:

BigInteger.Pow(BigInteger x, int y)

那么如何在上述方法中使用双数作为指数呢?

4

3 回答 3

5

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)))

于 2012-06-24T17:05:26.827 回答
2

我喜欢 carlosfigueira 的回答,但他的方法的结果当然只能在第一个(最重要的)15-17 位数字上是正确的,因为 aSystem.Double最终被用作乘数。

有趣的是,确实存在一种BigInteger.Log执行“逆”操作的方法。因此,如果您想计算Pow(7, 123456.78),理论上可以搜索所有BigInteger数字x以找到一个BigInteger.Log(x, 7)等于123456.78或接近于123456.78任何其他x类型的数字BigInteger

当然对数函数是递增的,所以你的搜索可以使用某种“二分搜索”(二分搜索)。我们的答案介于两者之间Pow(7, 123456)Pow(7, 123457)两者都可以精确计算。

如果需要,请跳过其余部分

现在,如果有多个整数的对数为123456.78,精度达到System.Double,或者实际上没有整数的对数达到特定Double值(理想Pow函数的精确结果是无理数)? 在我们的示例中,将有非常多的整数给出相同Double 123456.78的值,因为因子m = Pow(7, epsilon)(其中epsilon最小的正数123456.78 + epilon具有与其自身Double的表示不同的表示123456.78)足够大,以至于在真数之间会有非常多的整数答案和真实答案乘以m

从微积分中记住,数学函数的导数x → Pow(7, x)x → Log(7)*Pow(7, x),所以所讨论的指数函数图的斜率是Log(7)*Pow(7, 123456.78)。这个数字乘以上面epsilon仍然远大于一,所以有很多整数可以满足我们的需要。

实际上,我认为 carlosfigueira 的方法会给出一个“正确”的答案x,因为Log(x, 7)它与 a 具有相同的表示Double形式123456.78。但是有人试过吗?:-)

于 2012-06-24T19:27:56.417 回答
1

我将提供另一个希望更清楚的答案。关键是:由于精度System.Double限制在大约。15-17 位十进制数字,任何Pow(BigInteger, Double)计算的结果都将具有更有限的精度。因此,没有希望比 carlosfigueira 的回答做得更好。

让我用一个例子来说明这一点。假设我们要计算

Pow(10, exponent)

在这个例子中,我选择exponent双精度数

const double exponent = 100.0 * Math.PI;

这当然只是一个例子。的值exponent,以十进制表示,可以作为以下之一给出

314.159265358979
314.15926535897933
314.1592653589793258106510620564222335815429687500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000...

这些数字中的第一个是您通常看到的(15 位数字)。第二个版本exponent.ToString("R")包含 17 位数字。请注意, 的精度Double小于 17 位。上面的第三种表示是 的理论“精确”值exponent。请注意,这当然不同于第 17 位附近的数学数字 100π。

为了弄清楚Pow(10, exponent)应该是什么,我只是做BigInteger.Log10(x)了很多数字x来看看我如何重现exponent. 所以这里给出的结果只是反映了 .NET Framework 的BigInteger.Log10.

事实证明,任何BigInteger x

0x0C3F859904635FC0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
through
0x0C3F85990481FE7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF

Log10(x)等于exponent15 位的精度。同样,任何数从

0x0C3F8599047BDEC0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
through
0x0C3F8599047D667FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF

满足Log10(x) == exponent的精度Double。换句话说,后一个范围内的任何Pow(10, exponent)数字作为 的结果同样“正确” ,仅仅是因为 的精度exponent非常有限。

(插曲:一串0s 和Fs 表明 .NET 的实现只考虑 . 的最重要字节x。他们不想做得更好,正是因为Double类型的精度有限。)

现在,引入第三方软件的唯一原因是,如果您坚持exponent其解释为上面给出的十进制数字的三分之一。Double(类型允许你准确地指定你想要的数字真的是一个奇迹,对吧?)在这种情况下,结果Pow(10, exponent)将是一个无理数(但代数),带有一个不重复的小数尾部。如果没有舍入/截断,它就无法放入整数。PS!如果我们将指数作为实数 100π,那么数学上的结果会有所不同:我怀疑是某个超越数。

于 2012-07-10T14:49:22.467 回答