Выход NaN, как? - PullRequest
       25

Выход NaN, как?

0 голосов
/ 20 февраля 2020

Я пытаюсь закодировать ряд Тейлора, но я получаю 'nan' в качестве вывода в случае большого значения n (= 100). Где я делаю что-то не так?

#include<iostream>
#include<cmath>
using namespace std;

 int main(){

    int n;
    double x;

    cin >> n;
    cin >> x;

    long double temp_val = 1;
    int sign = 1;
    int power = 1;
    long long int factorial = 1;


    for(int i = 1 ; i < n ; i++){

        sign = sign* -1 ;
        power =  2*i;
        factorial  = factorial*(2*i)*(2*i-1);

        temp_val += sign*pow(x,power)/factorial;
    }
    cout<<temp_val;

}

Ответы [ 2 ]

5 голосов
/ 20 февраля 2020

Для больших n ваша программа имеет неопределенное поведение.

Вы вычисляете факториал 2n (т. Е. 200) в factorial. 200! есть в соответствии с Wolfram Alpha :

788657867364790503552363213932185062295135977687173263294742533244359449963403342920304284011984623904177212138919638830257642790242637105061926624952829931113462857270763317237396988943922445621451664240254033291864131227428294853277524242407573903240321257405579568660226031904170324062351700858796178922222789623703897374720000000000000000000000000000000000000000000000000

Для сравнения, типичное наибольшее значение, что long long int может держать это

9223372036854775807

(предполагается, что он 64-разрядный)

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

Но даже если вы измените тип переменной на unsigned, мало что изменится. Программа больше не будет иметь неопределенное поведение , но factorial на самом деле не будет содержать правильное значение. Вместо этого он будет возвращаться к нулю.

Даже если вы измените factorial на тип double, этого, вероятно, будет недостаточно при обычной реализации double для удержания этого значения. Ваша платформа может иметь тип long double, который больше double и может содержать это значение.

У вас будут похожие проблемы с pow(x, power), если x не близко к 1.

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

3 голосов
/ 20 февраля 2020

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

Вместо того, чтобы отслеживать оба термина по отдельности, вам нужно обновить результат и общее приращение. Я не буду предоставлять вам полное решение. В псевдокоде

double res = 0;
double delta = x;
int n = 1;
double sign = -1;
while ( ! stop_condition ) {
    delta *= (x / n);
    res += sign*delta;
    ++n;
    sign *= -1;
}
...