如何在C#中将BigInteger变为pow Double?

时间:2012-06-24 16:47:46

标签: c# math biginteger

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

  

BigInteger.Pow(BigInteger x,int y)

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

3 个答案:

答案 0 :(得分:5)

C#中没有任意精确的大数字支持,所以这不能直接完成。有一些替代方案(例如寻找第三方库),或者您可以尝试类似下面的代码 - 如果基数足够小,就像您的情况一样。

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

这个想法是使用大整数数学来计算指数的整数部分的幂,然后使用双(64位浮点)数学来计算分数部分的幂。然后,使用

这一事实
a ^ (int + frac) = a ^ int * a ^ frac

我们可以将两个值组合成一个大整数。但是简单地将double值转换为BigInteger会失去很多精度,所以我们首先将精度“移动”到bigInteger上(使用上面的循环,以及double类型使用52位的事实精确度),然后乘以结果。

请注意,结果是近似值,如果想要更精确的数字,则需要一个可以进行任意精度浮点数学运算的库。

更新:如果基数/指数足够小,功率将在double范围内,我们可以简单地执行Sebastian Piu建议的内容(new BigInteger(Math.Pow((double)@base, exp))

答案 1 :(得分:2)

我喜欢carlosfigueira的答案,但当然他的方法的结果只能在第一个(最重要的)15-17位数上正确,因为System.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)Double的{​​{1}}具有相同的代表性123456.78。但有人试过吗? : - )

答案 2 :(得分: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)的{​​{1}}进行了x,以了解我如何重现exponent。因此,此处提供的结果仅反映了.NET Framework的BigInteger.Log10

的实现

事实证明来自

的任何BigInteger x
0x0C3F859904635FC0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
through
0x0C3F85990481FE7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF

使Log10(x)等于exponent,精度为15位。同样,来自

的任何数字
0x0C3F8599047BDEC0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
through
0x0C3F8599047D667FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF

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

(插曲:0F的串联揭示了.NET的实现只考虑了x的最重要的字节。他们并不关心更好,正是因为Double类型具有这种有限的精度。)

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