Flavio's answer addresses a part of something that matters greatly for this kind of problem: rounding errors will kill you. If you look at the expression:
$$1-\frac13+\frac15-\frac17+...$$
This is the same as
$$\frac{3-1}{1\times3} + \frac{7-5}{5\times 7} + ...$$
It is easy to see that the nth term in this sequence is
$$\frac{2}{(4\times n-3)(4\times n-1)}$$
Now if you need a certain number of digits of precision, you first compute the number of terms you need - now that we have the terms in pairs, they are all contributing a positive amount. This converges extremely slowly... you can estimate this approximately by noticing that
$$\sum_n^\infty \frac{1}{x^2}\approx\int_{n-\frac12}^\infty \frac{dx}{x^2}=\frac{1}{n-\frac12}$$
So if you need the answer to be right to better than 5 digits, you need to sum at least 100,000 elements - and as the required accuracy goes up, you need to carry a LOT more significant figures. Alternatively, the smart person adds the estimated error term at the end - and gets awfully close to the right answer with not a lot of calculation. See below.
Now you may know that a single precision floating point number contains about 23 bits of precision - about 7 digits. This will really mess up your attempt to get even 4 digits of precision if you don't work backwards, starting with the smallest numbers (which means you can carry digits beyond the 7th decimal, until the sum gets bigger).
So the steps you take:
- Formulate the expression to be monotonic
- Determine the rate of convergence
- Find an expression for the number of terms you need to reach a certain precision
- Execute the calculations in an order that maximizes the accuracy
Only if all the above fail should you resort to brute force (using double precision and above, keeping going until you are sure your answer is no longer changing).
Of course, as others pointed out, for a "pure software" job, a lot of issues of properly structured code come into play - but if the task is one of numerical analysis, not just dropping a few lines of code, then the above considerations really matter.
For example, evaluating the expression for the first 100000 terms in the "forward" direction, using single precision (which emphasizes the problem), you get
pi=3.14138389
Doing it in the "backward" direction (starting with the smallest term) you get
pi=3.14158773
The difference: -0.00020385; they are different from the 5th significant figure onwards even though floating point precision "should be better than that" - but rounding errors compound! You can see that the "backward" method is getting close to 3.14159, while the "forward" method will never get there.
Here is some C code I wrote to demonstrate these points - note that the actual loop that does the piece you asked about takes just four lines. I realize you are looking for comments on your C# code, but the principles I outlined transcend the language you use.
// code that computes the value of pi by evaluating the first few terms
// of the infinite series
// pi = 4*(1/1-1/3+1/5-1/7+...)
//
// three important points:
// rounding error is reduced by evaluating starting with the small values
// rounding error is further reduced by pairing the values
// finally, an analytical estimate of the residual error is used to make the
// result significantly more accurate
#include <stdio.h>
#include <math.h>
int main(void) {
int N=100; // number of values to include
double PI=2*acos(0.0); // to confirm the accuracy later
float forwardSum, backwardSum, residualSum;
forwardSum=0;
for(int ii=1; ii<=N; ii++) {
forwardSum+=8./((4*ii-3.)*(4*ii-1.));
}
// ***** this is the code that does the heavy lifting *****
backwardSum=0;
for(int ii=N; ii>=1; ii--) {
backwardSum+=8./((4*ii-3.)*(4*ii-1.));
}
// backwardSum now contains our estimate of pi
// analytically we know the error should be
residualSum = 0.5/N;
printf("Starting with big terms, after %d pairs pi=%.8f\n", N, forwardSum);
printf("Starting with small terms, after %d pairs pi=%.8f\n", N, backwardSum);
printf("difference between directions: %.8f\n", backwardSum - forwardSum);
printf("estimated residual sum = %e\n", residualSum);
printf("updated estimate of pi: %.8f\n", backwardSum + residualSum);
printf("after correction, error in pi is %e\n", PI-(backwardSum +residualSum));
return 0;
}
The output of this code is:
Starting with big terms, after 100 pairs pi=3.13659286
Starting with small terms, after 100 pairs pi=3.13659263
difference between directions: -0.00000024
estimated residual sum = 5.000000e-03
updated estimate of pi: 3.14159274
after correction, error in pi is -8.742278e-08
Using single precision, the error is reduced to about 1 part in \$ 10^7 \$, just as we expect as the limit of accuracy. Incidentally, the error correction term is so good that you get the "right" answer with just 100 pairs of terms.
While brute force is sometimes all you need, a little analysis is not a bad trick to keep up your sleeve.