4

メソッドを使用してBigInteger.Pow10^12345.987654321 のようなものを計算しようとしましたが、このメソッドは次のような指数として整数のみを受け入れます:

BigInteger.Pow(BigInteger x, int y)

上記の方法で倍数を指数として使用するにはどうすればよいですか?

4

3 に答える 3

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

2つの値を1つの大きな整数に組み合わせることができます。ただし、double値をBigIntegerに変換するだけでは精度が大幅に低下するため、最初に精度をbigIntegerに「シフト」し(上記のループを使用し、double型が精度に52ビットを使用するという事実を使用)、次に乗算します。結果。

結果は近似値であることに注意してください。より正確な数値が必要な場合は、任意精度の浮動小数点演算を実行するライブラリが必要になります。

更新:底/指数が十分に小さく、べき乗がの範囲内にあるdouble場合は、Sebastian Piuが提案したことを簡単に実行できます(new BigInteger(Math.Pow((double)@base, exp))

于 2012-06-24T17:05:26.827 に答える
2

私は carlosfigueira の答えが好きですが、もちろん、彼の方法の結果は最初の (最上位の) 15 ~ 17 桁でしか正しくありません。これSystem.Doubleは、最終的に a が乗数として使用されるためです。

BigInteger.Log「逆」操作を実行するメソッドが存在することに注意してください。したがって、計算したい場合Pow(7, 123456.78)は、理論的には、すべてのBigInteger数値を検索して、他のタイプのと等しいか、それに近い数値xを 1 つ見つけることができます。BigInteger.Log(x, 7)123456.78123456.78xBigInteger

もちろん、対数関数が増えているので、検索にはある種の「二分検索」(二分検索) を使用できます。私たちの答えは と の間Pow(7, 123456)にあり、Pow(7, 123457)どちらも正確に計算できます。

必要に応じて残りをスキップします

123456.78さて、対数がである整数が の精度まで複数存在するSystem.Doubleかどうか、または対数がその特定の値に達する整数が実際に存在しないかどうかを事前に予測するにはどうすればよいでしょうかDouble(理想Pow関数が無理数であるという正確な結果) )? Double 123456.78この例では、因子m = Pow(7, epsilon)(はそれ自体の表現とは異なる表現を持つepsilonような最小の正の数) が十分に大きいため、同じ値を与える非常に多くの整数が存在します。答えと真の答えに を掛けたものです。123456.78 + epilonDouble123456.78m

微積分から、数学関数の導関数x → Pow(7, x)x → Log(7)*Pow(7, x)であることを思い出してください。したがって、問題の指数関数のグラフの傾きは になりますLog(7)*Pow(7, 123456.78)。この数に上記を掛けてepsilonも 1 よりはるかに大きいので、必要を満たす整数がたくさんあります。

実際、カルロスフィゲイラの方法は、 が aと同じ表現を持つというx意味で「正しい」答えを与えると思います。でも試した人いる?:-)Log(x, 7)Double123456.78

于 2012-06-24T19:27:56.417 に答える
1

できればもっと明確な別の答えを提供します。ポイントは次のとおりです。の精度はSystem.Double約に制限されているためです。10 進数で 15 ~ 17 桁の場合、Pow(BigInteger, Double)計算結果の精度はさらに制限されます。したがって、カルロスフィゲイラの答えよりもうまくいく見込みはありません。

これを例で説明しましょう。計算したいとしましょう

Pow(10, exponent)

この例では、exponent倍精度数を選択します

const double exponent = 100.0 * Math.PI;

もちろんこれはほんの一例です。の値はexponent、10 進数で、次のいずれかとして指定できます。

314.159265358979
314.15926535897933
314.1592653589793258106510620564222335815429687500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000...

これらの数字の最初の数字は、通常表示される数字 (15 桁) です。2 番目のバージョンは で作成されexponent.ToString("R")、17 桁が含まれています。Doubleの精度は 17 桁未満であることに注意してください。上記の 3 番目の表現は、 の理論上の「正確な」値ですexponent。もちろん、これは 17 桁目付近の数学的数 100π とは異なることに注意してください。

どうPow(10, exponent)あるべきかを理解するために、私は単純BigInteger.Log10(x)に多くの数字xを使って、どのように再現できるかを確認しましたexponent。したがって、ここに示す結果は、単に .NET Framework の の実装を反映したものですBigInteger.Log10

BigInteger xからのいずれかであることが判明しました。

0x0C3F859904635FC0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
through
0x0C3F85990481FE7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF

15 桁の精度にLog10(x)等しくなります。exponent同様に、

0x0C3F8599047BDEC0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
through
0x0C3F8599047D667FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF

Log10(x) == exponentの精度を満たしDoubleます。別の言い方をすれPow(10, exponent)、後者の範囲の数値は、 の精度exponentが非常に限られているため、 の結果として等しく「正しい」ということです。

(インターリュード: と の束は、.NET の実装が の最上位バイトのみを考慮していることを明らかにしています0。型の精度がこのように制限されているという理由だけで、.NET の実装はそれ以上のことを気にしません。)FxDouble

さて、サードパーティのソフトウェアを導入する唯一の理由は、それが上記の 10 進数の 3 番目として解釈されると主張する場合です。exponent(この型によって、必要な数を正確に指定できるのは本当に奇跡ですDoubleよね?) その場合、 の結果は、Pow(10, exponent)繰り返されない 10 進数の末尾を持つ無理数 (ただし代数的) の数になります。丸め/切り捨てなしでは整数に収まりませんでした。PS!指数を実数 100π とすると、結果は数学的に異なり、超越数になるのではないかと思います。

于 2012-07-10T14:49:22.467 に答える