Search code examples
c++precisionnumerical-methodspi

How to make a float in c++ without a decimal place limit


I have a c++ program which calculates Pi but then its value gets cut off after a few decimal places because of the decimal place limit on the float which Pi is stored in.

I've already tried using different data types instead of float but they still have a decimal place limit which is too small.

#include <iostream>
#include <fstream>
#include <sstream>
using namespace std;

int main(){
    cout << "Calculate Pi using the Nilakantha series" << endl << endl;

    unsigned long int iterations = 0;
    unsigned long int x = 0;
    unsigned long int y = 2;
    char z = 0;
    string fileName = "";
    int64_t Pi = {3.0f};

    cout << "Input the number of loop iterations(maximum of 4,294,967,295): ";
    cin >> iterations;
    cout << "Input the name of the file you want to write to: ";
    cin >> fileName;

    for(x = 0; x < iterations; x++){
        Pi += 4.0f / (y * (y + 1.0f) * (y + 2.0f));
        y += 2;
        Pi -= 4.0f / (y * (y + 1.0f) * (y + 2.0f));
    y += 2;
    }

    ofstream file;
    file.open(fileName + ".txt");
    file << Pi;
    file.close();

    cout << endl << "Calculation complete, check the file " << fileName << ".txt";

    cin >> z;
}

How do I remove the limit or use a method which stores the value of Pi without it getting cut off after a few decimal places?


Solution

  • There are a few big issues here. The first is that you're using std::cout to print the value, and std::cout only prints 6 digits by default. The second is that you declared Pi as an int64_t. That means you'll get at most 1 digit of precision, since Pi is an integer. You need to have it as a floating point type.

    So how many digits will you get from each type?

    • 6 digits from float
    • 15-16 digits from double
    • 20 digits from long double on clang and gcc, but only 15-16 in Visual Studio
    • Around 30 digits from GCC's __float128, although that one only works on GCC

    If you want more precision than that, you're going to have to use a high-precision arithmetic library that gives you more digits by simulating it. It won't be as fast, but it'll do the job.

    You can play around with the amount of precision with each type using this code. I templated calculatePi on the type, so you can just stick in float, double, long double, or __float128. It'll print 20 digits. This code should print all 20 digits correctly for __float128.

    #include <cstdio>
    using namespace std;
    
    template<class Float>
    Float calculatePi(size_t iterations) {
        Float y = 2;
        Float Pi = 3;
        const Float two = 2;
        const Float four = 4;
        const Float one = 1;
    
        for(size_t i = 0; i < iterations; i++) {
            Pi += four / (y * (y + one) * (y + two));
            y += two;
            Pi -= four / (y * (y + one) * (y + two));
            y += two;
        }
        return Pi; 
    }
    int main(){
        long double result = calculatePi<long double>(1000000); 
    
        printf("Expected: 3.14159265358979323846\n"); 
        printf("Actual:   %.20Lf", result); 
    }