1

I'm trying to write a program that uses the series to compute the value of PI. The user will input how far it wants the program to compute the series and then the program should output its calculated value of PI. I believe I've successfully written the code for this, however it does not do well with large numbers and only gives me a few decimal places. When I tried to use cout << fixed << setprecision(42); It just gave me "nan" as the value of PI.

int main() {

    long long seqNum; // sequence number users will input
    long double val; // the series output

    cout << "Welcome to the compute PI program." << endl; // welcome message
    cout << "Please inter the sequence number in the form of an integer." << endl;
    cin >> seqNum; // user input

    while ( seqNum < 0) // validation, number must be positive
    {
        cout << "Please enter a positive number." << endl;
        cin >> seqNum;
    } // end while

    if (seqNum > 0)
    {
        for ( long int i = 0; i < seqNum; i++ )
        {
            val = val + 4*(pow(-1.00,i)/(1 + 2*i)); // Gregory-Leibniz sum calculation
        }// end for


        cout << val;
    } // end if

    return 0;
}

Any help would be really appreciated. Thank you

walnut
  • 21,629
  • 4
  • 23
  • 59
sydneeod
  • 37
  • 4
  • A typical `long double` can only represent a value to about 35 significant figures. For example, IEEE754 quadruple-precision floating point - which is a pretty commonly used format used for a `long double` - only guarantees up to 34 significant digits. Which means a requirement to 42 decimal places of pi is unachievable with most real implementations. Also, in your code, `val` is uninitialised and the first usage accesses its value - which gives undefined behaviour. – Peter Feb 10 '20 at 03:12
  • I see, so even if I were to use setprecision(42), anything after the 35th digit would be goop? Also, I'm not sure what you mean by my val being uninitialized. I thought I did so at the top by declaring it a long double – sydneeod Feb 10 '20 at 03:21
  • The definition `long double val` means `val` is defined but it is uninitialised. Accessing the value of an uninitialised variable gives undefined behaviour. The first usage of `val`, in a nested loop, is `val = val + 4*(pow(-1.00,i)/(1 + 2*i));` which means that the new value of `val` is calculated using its previous value - so the first time that happens, `val` is uninitialised, and the behaviour is undefined. And (this is a common but flawed assumption you are making) `long double val` is not required to initialise `val` to zero, or to any other particular value. – Peter Feb 10 '20 at 03:26
  • @Peter long double is typically not IEEE754 quadruple precision but the 80-bit extended precition on x86 – phuclv Feb 10 '20 at 06:27
  • `pow(-1.00,i)` is an **extremely inefficient** way to get alternating sign. Just use something like `i % 2 == 0 ? 1 : -1` – phuclv Feb 11 '20 at 02:32

2 Answers2

1

Your problem involves an elementary, fundamental principle related to double values: a double, or any floating point type, can hold only a fixed upper limit of significant digits. There is no unlimited digits of precision with plain, garden-variety doubles. There's a hard, upper limit. The exact limit is implementation defined, but on modern C++ implementations the typical limit is just 16 or 17 digits of precision, not even close to your desired 42 digits of precision.

#include <limits>
#include <iostream>

int main()
{
    std::cout << std::numeric_limits<double>::max_digits10 << std::endl;
    return 0;
}

This gives you the maximum digits of precision with your platform/C++ compiler. This shows a maximum of 17 digits of precision with g++ 9.2 on Linux (max_digits10 is C++11 or later, use digits10 with old C++ compilers to show a closely-related metric).

Your desired 42 digits of precision likely far exceed what your modest doubles can handle. There are various special-purpose math libraries that can perform calculations with higher levels of precision, you can investigate those, if you wish.

Sam Varshavchik
  • 114,536
  • 5
  • 94
  • 148
1

You did not initialize or assign any value to val, but you are reading it when you get to the first iteration of

val = val + 4*(pow(-1.00,i)/(1 + 2*i));

This cause your program to have undefined behavior. Initialize val, probably to zero:

long double val = 0; // the series output

That aside, as mentioned in the answer of @SamVarshavchik there is a hard limit on the precision you can reach with the built-in floating point types and 42 places significance is almost certainly outside of that. Similarly the integer types that you are using are limited in size to probably at most 2^64 which is approximately 10^19.

Even if these limits weren't the problem, the series requires summation of roughly 10^42 terms to get PI to a precision of 42 places. It would take you longer than the universe has been around to calculate to that precision with all of earths current computing power combined.

walnut
  • 21,629
  • 4
  • 23
  • 59