Search code examples
javaloopsbigdecimalpi

Calculating pi using BigDecimals and the Nilakantha series does not work for more than 10 decimal places


Here's my code:

import java.math.BigDecimal;

public class Challenge6 {

    private static final String PI = "3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679821480865132823066470938446095505822317253594081284811174502841027019385211055596446229489549303819644288109756659334461284756482337867831652712019091456485669234603486104543266482133936072602491412737245870066063155881748815209209628292540917153643678925903600113305305488204665213841469519415116094330572703657595919530921861173819326117931051185480744623799627495673518857527248912279381830119491298336733624406566430860213949463952247371907021798609437027705392171762931767523846748184676694051320005681271452635608277857713427577896091736371787214684409012249534301465495853710507922796892589235420199561121290219608640344181598136297747713099605187072113499999983729780499510597317328160963185950244594553469083026425223082533446850352619311881710100031378387528865875332083814206171776691473035982534904287554687311595628638823537875937519577818577805321712268066130019278766111959092164201989380952572010654858632788659361533818279682303019520353018529689957736225994138912497217752834791315155748572424541506959508295";

    public static BigDecimal calculatePi(int decimals) {

        BigDecimal pi = new BigDecimal(3);
        boolean toggle = true;
        boolean enough = false;
        int i = 2;

        while (!enough) {
            BigDecimal num = new BigDecimal(
                    (double) (4 / (double) (i * (i + 1) * (i + 2))));
            System.out.println(i / 2);

            if (toggle) {
                pi = pi.add(num);
                toggle = false;

            } else {
                pi = pi.subtract(num);
                toggle = true;
            }
            i += 2;

            try {
                if (PI.substring(0, decimals + 2).equals(
                        ("" + pi).substring(0, decimals + 2))) {
                    enough = true;
                } else {

                }
            } catch (Exception ex) {
                continue;
            }
        }
        return pi;
    }

    public static void main(String[] args) {
        System.out.printf("Pi: %.30s", calculatePi(10));
    }
}

I know it's not perfect, but it's just for fun. I'm just wondering why my program can perfectly generate pi up to 10 decimal places in just over 700 iterations, but when I try to calculate any more than 10, it goes forever (at least I assume forever). Is this just due to floating point division or something? If so, why does it just stop working right after 10? Also, if you run the program while printing out pi every iteration, it turns to something like 3.39. Is BigDecimal having issues here, or is there something wrong with my code? Any help would be cool.

P.S. - I only really need 30 decimal places.


Solution

  • When you use expression "new BigDecimal( (double) (4 / (double) (i * (i + 1) * (i + 2))))" you use double, not BigDecimal. And using double ins't posible to have sufficient scale for callculation more then 10 decimal places.

    Use following code

    public static BigDecimal calculatePi(int decimals) {
        BigDecimal pi = new BigDecimal(3);
        boolean toggle = true;
        boolean enough = false;
        int i = 2;
    
        while (!enough) {
            BigDecimal a = new BigDecimal(4); // All operation must be using only BigDecimal 
            BigDecimal b = new BigDecimal(i).multiply(new BigDecimal(i + 1)).multiply(new BigDecimal(i + 2));
            BigDecimal num = a.divide(b, 100, RoundingMode.FLOOR);
            System.out.println(i / 2);
    
            if (toggle) {
                pi = pi.add(num);
                toggle = false;
    
            } else {
                pi = pi.subtract(num);
                toggle = true;
            }
            i += 2;
    
            if (PI.substring(0, decimals + 2).equals(("" + pi).substring(0, decimals + 2))) {
                enough = true;
            }
        }
        return pi;
    }
    

    P.S. I check this code for 20 decimal places, but for 30 decimal places you need waiting a lot of time, I think