Home > Mobile >  Output is nan while it shouldn't be
Output is nan while it shouldn't be

Time:10-13

I need to calculate the following:

S= 1- x^2 / 2! x^4 / 4! - x^6 / 6! ... (-1)^n * x^2n / (2n)!

Where n is between 1 and 100, and x is a double.

I have the following code:

unsigned int factorial (unsigned int n)
{
    if (n == 0)
        return 1;
    return n * factorial(n - 1);
}

double exFive(int n, double x)
{
    double s = 1;

    for (int i = 1; i <= n;   i)
    {
        int j = 2 * i; 
        s = s   pow(-1, i) * pow(x, 2*i) / factorial(j); //problem is here I guess
    }

    return s;
}

void fiveIO()
{
    int n = 1;
    double x;
   
    cout << "Input n: ";
    cin >> n;
    while ((n < 1) || (n > 100))
    {
        cout << "Wrong number, input again: ";
        cin >> n;
    } 

    cout << "Input: ";
    cin >> x;

    cout << "Result is " << fixed << setprecision(2) << exFive(n, x);
}

It works however the result is nan where n is above ~15.. but I don't know why. I would presume it’s the FiveX function.

So, for instance, n = 3, x = 5 outputs -7.16 (which is correct), but n = 50, x = 5 outputs "nan".. is it because the output is too large of a number? But then how am I supposed to do it?

CodePudding user response:

Avoid int overflow (undefined behavior (UB)) in factorial(j) Typical 32-bit int can only hold result of factorial(12).

Improve loop computation by calculating the term based on its prior value.

//for (int i = 1; i <= n;   i) {
//    int j = 2 * i; 
//    s = s   pow(-1, i) * pow(x, 2*i) / factorial(j); //problem is here I guess
//}

// Something like
double xx = x*x;
double term = 1.0;
for (int i = 1; i <= n;   i) {
    int j = 2 * i; 
    term *= -xx / ((j-1)*j);
    s  = term;
}

A more advanced approach would calculate the sum in reverse to minimize computation errors, yet a lot depends on the allowable range of x, something not constrained by OP.

CodePudding user response:

For the formula:

S= 1- x^2 / 2! x^4 / 4! - x^6 / 6! ... (-1)^n * x^2n / (2n)!

I submit this implementation scales slightly better since it avoids multiplying everything at once.

double exFive(int n, double x)
{
    double s = 1;

    bool subtract = true;

    for (int i = 1; i <= n; i  )
    {
        double inner = 1;
        int n2 = i * 2;
        for (int j = 1; j <= n2; j  )
        {
            inner *= (x / j);
        }

        if (subtract)
        {
            s = s - inner;
        }
        else
        {
            subtract = s   inner;
        }
        subtract = !subtract;
    }
    return s;
}

Sample Run:

Input n: 100
Input: 94
Result is -4417.00

The implementation above, when it needs to compute say (x^n)/(n!). Instead of computing

T  = (x*x*x*x....*x*x*x) / (1*2*3*4*5*6*....*(N-1)*N)`

In the above expression, both the numerator and denominator will overflow when attempting to compute either using pow of factorial functions. So instead it will compute it as:

T = (x/1) * (x/2) * (x/3) * (x/4) * (x/5) * (x/6)

This avoids using factorial and pow functions. And since each iteration of the outer loop is alternating subtraction or addition operations, the chance of overflow remains smaller.

  • Related