Программа на C ++, которая вычисляет ln для заданной переменной x без использования готовых функций

0

Я искал уравнение, которое вычисляет ln числа x и выясняет, что это уравнение:

Изображение 174551

и я написал этот код для его реализации:

double ln = x-1 ;
    for(int i=2;i<=5;i++)
    {
        double tmp = 1 ;
        for(int j=1;j<=i;j++)
            tmp *= (x-1) ;
        if(i%2==0)
            ln -= (tmp/i) ;
        else
            ln += (tmp/i) ;
    }
    cout << "ln: " << setprecision(10) << ln << endl ;

но, к сожалению, я получаю результаты, совершенно отличные от вывода на моем калькуляторе, особенно для больших чисел, может ли кто-нибудь сказать мне, где проблема?

  • 0
    приведите реальные примеры проблемных входов и выходов, пожалуйста.
  • 4
    Для больших значений x tmp *= (x-1) наиболее вероятно переполняется в течение нескольких итераций. Кроме того, ваша формула верна только для x между 0 и 2.
Показать ещё 6 комментариев
Теги:
math
output
natural-logarithm

5 ответов

1
Лучший ответ

Уравнение, на которое вы ссылаетесь, представляет собой бесконечную серию, которая подразумевается эллипсисом, следующим за основной частью уравнения, и, как указано более подробно предыдущей формулировкой на той же странице:

Изображение 174551

В вашем случае вы вычисляете только первые четыре условия. Более поздние термины добавят небольшие уточнения к результату, чтобы приблизиться к фактическому значению, но в конечном счете для вычисления всех бесконечных шагов потребуется бесконечное время.

Однако, что вы можете сделать, это приблизительный ответ на что-то вроде:

double ln(double x) {
  // validate 0 < x < 2
  double threshold = 1e-5;  // set this to whatever threshold you want
  double base = x-1;        // Base of the numerator; exponent will be explicit
  int den = 1;              // Denominator of the nth term
  int sign = 1;             // Used to swap the sign of each term
  double term = base;       // First term
  double prev = 0;          // Previous sum
  double result = term;     // Kick it off

  while (fabs(prev - result) > threshold) {
      den++;
      sign *=- 1;
      term *= base;
      prev = result;
      result += sign * term / den;
  }

  return result;
}

Осторожно: я на самом деле не проверял это, поэтому может потребоваться некоторая настройка.

То, что это делает, вычисляет каждый термин до тех пор, пока абсолютная разница между двумя последовательными терминами будет меньше определенного порога, который вы устанавливаете.

Теперь это не особенно эффективный способ сделать это. Лучше работать с функциями используемого вами языка (в данном случае C++) позволяет вычислить естественный журнал (который, как мне кажется, уже показал вам другой плакат). Но может быть какая-то ценность, пытаясь понять, как это работает.

Кроме того, как отмечает Barak manos ниже, эта серия Тейлора сходится только в диапазоне (0, 2), поэтому вам нужно будет проверить значение x в этом диапазоне, прежде чем пытаться выполнить фактическое вычисление.

  • 1
    Формула верна только для x между 0 и 2. См. En.wikipedia.org/wiki/… .
  • 0
    @barakmanos Спасибо ... да, ты прав. Я обновил комментарий, описывающий то, что должно быть проверено перед вычислением ряда.
Показать ещё 1 комментарий
0

Не помешало бы использовать long и long double вместо int и double. Это может получить немного большую точность на некоторых более крупных значениях. Кроме того, ваша серия, расширяющая только 5 уровней, также ограничивает вашу точность.

Использование такой серии в основном является приближением логарифмического ответа.

0

Эта версия должна быть несколько быстрее:

double const scale = 1.5390959186233239e-16;
double const offset = -709.05401552996614;

double fast_ln(double x)
{
    uint64_t xbits;
    memcpy(&xbits, &x, 8);
    // if memcpy not allowed, use 
    //       for( i = 0; i < 8; ++i ) i[(char*)xbits] = i[(char*)x];
    return xbits * scale + offset;
}

Хитрость заключается в том, что в ней используется 64-разрядное целочисленное * 64-разрядное умножение с плавающей запятой, которое включает преобразование целого числа в плавающую точку. Указанное представление с плавающей запятой аналогично научной нотации и требует логарифма, чтобы найти соответствующий показатель степени... но это делается исключительно в аппаратном обеспечении и очень быстро.

Однако он выполняет линейное приближение в каждой октаве, что не очень точно. Использование таблицы поиска для этих бит будет намного лучше.

0

Я считаю, что естественный журнал в C++ языке - это просто журнал

  • 7
    Хотя это на самом деле правильно, я не думаю, что это отвечает на вопрос.
  • 0
    @gniourf_gniourf: отмечать это как NAA было бы неуместно. Это попытка ответа, однако она неправильно поняла вопрос.
Показать ещё 4 комментария
0

Эта формула не будет работать для больших ресурсов, потому что это потребует от вас учета члена самой высокой степени, которого вы не можете, потому что они бесконечны. Он будет работать только для небольших входов, где важны только первые условия вашей серии.

Вы можете найти способы сделать это здесь: http://en.wikipedia.or/wiki/Pollard%27s_rho_algorithm_for_logarithms

и здесь: http://www.netlib.org/cephes/qlibdoc.html#qlog

  • 0
    Более того, рассчитывать его только на 5 слагаемых даже для небольших чисел будет далеко. Вам понадобится намного больше терминов, чтобы приблизиться. Вам действительно нужно найти лучшую формулу (та, которая сходится быстрее и использует небольшие числа, например, ln (10) означает, что 20-й член вычисляет 9 ^ 20/20 или 12157665459056928801/20! Если вы хотите выполнить расчет с двойной точностью Числа с плавающей запятой в C ++ в разумные сроки и в разумных пределах входных значений.
  • 1
    @CXJ: Многие разложения в ряды Тейлора имеют факториал в знаменателе, но для log факториал отменяет единицу в числителе ... В любом случае ряд расходится при x > 2 , поэтому вычисление pow(9,20)/20 совершенно бессмысленно ,
Показать ещё 2 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню