Originally Posted by
sci4me
...BigDecimal...
That's probably a good thing to investigate, since you are wanting more precision in the terms than Java's built-in double precision variables can give you.
Originally Posted by
sci4me
...and BigInteger to do this and it didn't really work out...
I don't see the need for BigInteger. Every term is a floating point number. When you add successive terms you get an approximation that is a floating point number. So, just do everything with floating point arithmetic. Concentrate on BigDecimal (or other arbitrary precision floating point packages that you might run across).
What do you mean it didn't "work out"? It "works out" if you "do it right."
Originally Posted by
sci4me
I am wondering, how do I convert this...
OK, I'll give it a shot:
- Create BigDecimal objects for numerator, denominator, sum, and term with initial values 4.0, 1.0, 0.0, and 4.0
- You very well might find use for a BigDecimal object initialized to 2.0, so that you can add 2.0 to the denominator each time through the loop. (I would probably call it bigTwo, or some such thing.)
- Declare a boolean variable positiveSign that will make it easy to alternate the sign of the term that contributes to the sum each time through the loop. initialize it to true, since the first term will be added to sum inside the loop.
- Decide how many terms you would like to use in your approximation. Start with a small number so that you can verify the accuracy of the term and the cumulative sum at each step of the program.
- Decide how many decimal places of precision for which you want the divisions to be approximated and create a MathContext object for this value.
Here's pseudo-code for the main() method:
Declare and initialize int variables numTerms and precision.
Create a MathContext object with your value of precision.
Declare and initialize BigDecimal objects
bigTwo Initial value 2.0
sum Initial value 0.0
numerator Initial value 4.0
denominator Initial value 1.0
term Initial value numerator divided by denominator
Declare and initialize the boolean variable:
positiveSign Initial value true
Here's the loop:
for (int i = 0; i < numTerms; i++)
BEGIN LOOP
IF positiveSign THEN
sum = sum plus term // Use BigDecimal add() method
ELSE
sum = sum minus term // Use BigDecimal subtract() method
END IF
Print out value of sum.
// Calculate value of next term. Note that for alternating
// convergent series, truncation error is less than or equal
// to the absolute value of the next term.
denominator = denominator plus 2 // Use BigDecimal add(bigTwo)
term = numerator divided by denominator // Use BigDecimal divide() with mc context
Print value of term. (This is an upper bound of the error of the sum so far.)
Set positiveSign equal to !positiveSign
END LOOP
Now, suppose we want 10 decimal digit precision to be maintained for the division operations, and suppose we want to print the first four partial sums
The output might look like:
Partial sum 1:
Approx: 4
|Err| < 1.333333333
Partial sum 2:
Approx: 2.666666667
|Err| < 0.8
Partial sum 3:
Approx: 3.466666667
|Err| < 0.5714285714
Partial sum 4:
Approx: 2.8952380956
|Err| < 0.4444444444
It's very important that you verify the validity of these values before going on to bigger and better things.
If you like it so far (that is, if you think it is giving the results that you expected), then, try something like precision = 100 decimal digits and ten thousand terms.
Here are the last few of the 10000 partial sums:
Partial sum 9997:
Approx: 3.1416926835985457141409486174398088488002669437238313470218194294878764930799823190509708376226708769092
|Err| < 0.0002000500125031257814453613403350837709427356839209802450612653163290822705676419104776194048512128032
Partial sum 9998:
Approx: 3.1414926335860425883595032560994737650293242080399103667767581641715474108094146771404932182178196641060
|Err| < 0.0002000300045006751012651897784667700155023253488023203480522078311746762014302145321798269740461069160
Partial sum 9999:
Approx: 3.1416926635905432634607684458779405350448265333887126871248103720027220870108448916726730451918657710220
|Err| < 0.0002000100005000250012500625031251562578128906445322266113305665283264163208160408020401020051002550128
Partial sum 10000:
Approx: 3.1414926535900432384595183833748153787870136427441804605134798054743956706900288508706329431867655160092
|Err| < 0.0001999900004999750012499375031248437578121093945302734863256837158142092895355232238388080595970201490
See how it goes?
The program has done a lot of work. (Who cares? That's why we have computers, right? Let them do the work.)
However...
After ten thousand terms, the approximation is only correct to four significant decimal digits.
The problem isn't with the "accuracy" of the infinite series you are using. It's "accurate." The problem is that it will take a lot of terms (a
whole lot of terms) to converge to a value that has more precision than the 16 or 17 significant decimal digits that you get with double precision Java variables.
Exercise for the student:
How many terms of the series will be required to get an approximate value of Pi that is accurate to 20 significant decimal digits.
That's 19 decimal places accuracy.
Question:
Given that the absolute error after n terms is bounded by the value of the next term (i.e. 4/(2*n+1)), how many terms will be required to guarantee that the error is less than 0.5 times 10 to the -19 power?
Short answer:
A bunch.
Intermediate answer:
A really big bunch.
Next question:
Is there a better way?
Answer:
There are about a million ways. Maybe more. It happens that your series, equal to the Taylor series expansion of 4*arctan(1.0), is one of the slowest of them all. Really slow.
People have been developing faster-converging approximations for hundreds of years. Maybe more. If you are really interested, then do some research and knuckle down to learning some math.
Maybe start here:
Numerical Approximations of Pi
If you think it's better to spend your time doing something else until you have been exposed to enough mathematical rigor to read the literature, then that's OK too.
Cheers!
Z