Calculate the sum of a series with a given accuracy

#include <iostream>
#include <cstring>
using namespace std;
int main() {
system("chcp 1251>nul");
int i = 1;
int x;
cin >> x;
if (abs(x) >= 1) { cout << "The series is divergent"; }
else {
double eps = 0.000001;
double sum = 0;
double lastElementOfSequence=1;
do {
sum += lastElementOfSequence;
lastElementOfSequence *= -1 * x * (2 * i + 3) / (2 * i);
i++;
} while (fabs(lastElementOfSequence) > eps);
cout.precision(17);
cout << "sum=" << sum << endl;
}
system("pause>nul");
return 0;
}


$$S=(1+x)^{-\frac{5}{2}}=1-\frac{5}{2}x+\frac{5\cdot7}{2\cdot4}x^2-\frac{5\cdot7\cdot9}{2\cdot4\cdot6}x^3+\frac{5\cdot7\cdot9\cdot11}{2\cdot4\cdot6\cdot8}x^4$$ $$where\ |x|<1$$

The task in the image, and the code from above I will ask to check up whether the correctly solved task I will be grateful, but if an error that specify please or correct.

• Better use std::numeric_limits<double>::epsilon instead of eps. And of course, don't use using namespace std; it's a bad habit, because it can lead to a conflict of names, use std::<something> instead. – Bogdasar Mar 7 at 15:00
• @Bogdasar But the question of whether the solution of the problem is correct, I will correct the rest – Andrij Matviiv Mar 7 at 15:09
• @Bogdasar, I think the eps is the "given accuracy" of the title, so std::numeric_limits<double>::epsilon would be the wrong choice here. – Toby Speight Mar 7 at 15:21
• @TobySpeight yes you are right – Andrij Matviiv Mar 7 at 15:23
• This part: -1 * x * (2 * i + 3) / (2 * i) is fully integer calculation. Does not seem like it was intended to be integer. To resolve this you can do either 1) add double conversion somewhere appropriate, e.g. -1 * x * double(2 * i + 3) / (2 * i) 2) change type of i to double (note: double can represent ~53 bits of whole numbers exactly, so no precision will be lost on increments). Edit: Also your formula says abs(x) should be less than one, but you enter x as integer. The only integer value, which abs is less than 1 is 0. Did you mean to use fractional number for x? – stepan Mar 7 at 20:08

We're including the wrong headers. We don't need <cstring>, but we do need <cmath> (for std::fabs).

Don't do this:

using namespace std;


That might not be so harmful in small programs, but it's a bad habit have because it makes larger programs more difficult to follow, and it increases the risk of identifier collisions, especially as the standard library continues to grow.

Never ignore the return value from std::system(). There are two uses of this function, and both of them are going to fail here, since I don't have either of the programs installed. Is it right that they both create a file called nul? Or did you just misspell /dev/null?

Never ignore the return value from stream-reading operations. std::cin >> x leaves x uninitialised (before C++11) or zero (C++11 onwards) if it can't perform the conversion.

I don't think you wanted an integer as x anyway - shouldn't it be double?

Error messages should go to std::cerr, not std::cout.

Instead of multiplying i by 2 all the time, consider incrementing i in steps of 2.

The addition is being done in the wrong order. When adding floating-point numbers, we want to accumulate the smallest values first, so as not to lose precision.

Instead of using the precision() function of a stream, consider using <iomanip>. 17 decimal digits of precision seems too large, given that the value of eps means we won't achieve 6 digits.

Don't use std::endl except when you genuinely need to flush the stream. That unnecessarily slows programs down.

Here's a version that's modified to fix the above problems:

#include <cmath>
#include <limits>
#include <iomanip>
#include <iostream>
#include <numeric>
#include <vector>

// FIXME - make this a parameter
constexpr double precision = 0.000'001;

int main()
{
double x;
std::cin >> x;
if (!std::cin) {
std::cerr << "Invalid input\n";
return 1;
}

if (std::fabs(x) >= 1) {
std::cerr << "The series is divergent above |x|=1\n";
return 1;
}

std::vector<double> terms;
double term = 1;
for (auto i = 2ul;  std::fabs(term) >= precision;  i += 2) {
terms.push_back(term);
term *= -x * (i + 3) / i;
if (i > std::numeric_limits<decltype(i)>::max() - 2) {
// overflow
break;
}
}
auto sum = std::accumulate(terms.crbegin(), terms.crend(), 0.0);

std::cout << std::setprecision(7) << "sum=" << sum << '\n';
}


As suggested by greybeard in comments, the way the function converges means we can get a good estimate of the rest of the series by halving the last term that we consider, and we can terminate when the sum of two consecutive elements is within our precision value:

std::vector<double> terms;
double term = 1;
std::size_t i = 2;
do {
terms.push_back(term);
term *= -x * (i + 3) / i;
i += 2;
} while (std::abs(term + terms.back()) >= precision);

// term/2 is a good approximation to the rest of the series
terms.push_back(term / 2);

• Yes, but I meant either the right idea or the right solution (calculate the sum of the series) and if something is wrong, you could not correct – Andrij Matviiv Mar 7 at 15:25
• You posted on Code Review. That means that any aspect of the code posted is fair game for feedback and criticism. – Toby Speight Mar 7 at 15:30
• I'm sorry I didn't know, I'm guilty of not correctly specifying the sign for which a review is required – Andrij Matviiv Mar 7 at 15:34
• (all non-2 prime factors of the denominator appear at least as often in the numerator: can this be used to improve accuracy? How?) With x close to one, convergence will be slow: is int i safe? – greybeard Mar 8 at 0:20
• (While summing up terms in increasing abs value is better than the reverse, I'm afraid it isn't guaranteed to help with loads of similar values. It is safe with alternating values as given here.) Oh, and I guess the sign Andrij Matviiv referred to is that of the exponent in $(1+x)^{-\frac 5 2}$. – greybeard Mar 8 at 15:41