как получить BigInteger для двойного pow в C #?

Я попытался использовать метод BigInteger.Pow для вычисления чего-то вроде 10 ^ 12345.987654321, но этот метод принимает только целые числа в качестве экспоненты, например:

BigInteger.Pow (BigInteger x, целое число y)

Итак, как я могу использовать двойное число в качестве показателя в приведенном выше методе?


person Siyamak Shahpasand    schedule 24.06.2012    source источник


Ответы (3)


В 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

мы можем объединить два значения в одно большое целое число. Но простое преобразование двойного значения в BigInteger приведет к большой потере точности, поэтому мы сначала «переносим» точность на bigInteger (используя цикл выше и тот факт, что тип double использует 52 бита для точности), а затем умножение результата.

Обратите внимание, что результат является приблизительным. Если вы хотите получить более точное число, вам понадобится библиотека, которая выполняет вычисления с плавающей запятой произвольной точности.

Обновление: если основание / показатель степени достаточно малы, чтобы мощность находилась в диапазоне double, мы можем просто сделать то, что предложил Себастьян Пиу (new BigInteger(Math.Pow((double)@base, exp)))

person carlosfigueira    schedule 24.06.2012
comment
Я просто из любопытства спрашиваю, почему новое new BigInteger(Math.Pow(10, 123.123)); неправильно? - person Sebastian Piu; 24.06.2012
comment
Он хотел выполнить 10 ^ 12345.123, что выходит за пределы диапазона double (результат Math.Pow). Я уменьшил его, чтобы увидеть результат в моем консольном приложении, но я снова увеличу его, чтобы было понятно. - person carlosfigueira; 24.06.2012
comment
Тогда вам понадобится сторонняя библиотека. Этого не существует в ядре .NET Framework. - person carlosfigueira; 24.06.2012
comment
@carlosfigueira знаете ли вы какую-либо стороннюю библиотеку, которая может решить эту проблему? - person Siyamak Shahpasand; 24.06.2012
comment
Нет. Вы можете оставить этот вопрос открытым и дождаться других ответов, но здесь были и другие (например, stackoverflow.com/q/621684 / 751090, stackoverflow.com/q/2863388/751090), и один ответ, указывающий на библиотека имеет неработающую ссылку. - person carlosfigueira; 24.06.2012
comment
Интересно! Но вам не обязательно выполнять точную передачу в цикле. Вы можете использовать константу 2 для 52-й степени (или 53-й, может быть, даже лучше), которая может быть представлена ​​точно как Double и как BigInteger. Затем разделите temp один раз на эту константу и умножьте temp2 один раз на эту константу. - person Jeppe Stig Nielsen; 24.06.2012

Мне нравится ответ Карлосфигейры, но, конечно, результат его метода может быть правильным только для первых (наиболее значимых) 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, по-прежнему намного больше единицы, поэтому есть много целых чисел, удовлетворяющих наши потребности.

На самом деле, я думаю, что метод Карлосфигейры даст "правильный" ответ x в том смысле, что Log(x, 7) имеет то же представление, что и Double, и 123456.78. Но кто-нибудь пробовал? :-)

person Jeppe Stig Nielsen    schedule 24.06.2012

Я дам другой ответ, который, надеюсь, будет более ясным. Дело в следующем: Поскольку точность 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. Обратите внимание, что это, конечно, отличается от математического числа 100π рядом с 17-й цифрой.

Чтобы выяснить, каким Pow(10, exponent) должно быть, я просто сделал BigInteger.Log10(x) на большом количестве чисел x, чтобы посмотреть, как я могу воспроизвести exponent. Таким образом, представленные здесь результаты просто отражают реализацию BigInteger.Log10 в .NET Framework.

Получается, что любой BigInteger x из

0x0C3F859904635FC0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
through
0x0C3F85990481FE7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF

делает Log10(x) равным exponent с точностью до 15 цифр. Аналогично любое число из

0x0C3F8599047BDEC0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
through
0x0C3F8599047D667FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF

удовлетворяет Log10(x) == exponent с точностью Double. Другими словами, любое число из последнего диапазона равно "правильному" как результат Pow(10, exponent) просто потому, что точность exponent настолько ограничена.

(Интерлюдия: связки 0s и Fs показывают, что реализация .NET учитывает только наиболее значимые байты x. Они не заботятся о том, чтобы добиться большего, именно потому, что тип Double имеет эту ограниченную точность.)

Единственная причина для внедрения стороннего программного обеспечения - это если вы настаиваете на том, чтобы exponent интерпретировался как треть десятичных чисел, указанных выше. (Это действительно чудо, что тип Double позволил вам указать именно то число, которое вы хотели, а?) В этом случае результатом Pow(10, exponent) будет иррациональное (но алгебраическое) число с хвостом из никогда не повторяющихся десятичных знаков. Он не мог поместиться в целое число без округления / усечения. PS! Если мы возьмем показатель степени за действительное число 100π, математически результат будет другим: я подозреваю, что это какое-то трансцендентное число.

person Jeppe Stig Nielsen    schedule 10.07.2012