Simple C Calculator


The following source code was generated using OpenAI’s GPT-4.0. This program is designed to be compiled on Debian-based Linux distributions, including Ubuntu, Raspbian, and Debian. Using the Gauss-Legendre algorithm, It accepts two parameters: the first specifies the number of digits of π you wish to calculate, while the second is purely aesthetic, allowing you to control the speed of the calculation by introducing a delay. In theory, this program can generate π to as many digits as your processor architecture can support. It also has a switch that indicates not to round up the final digit of precision in the output.

Code

// ==========================================
// Simple C Pi Calculation Program - Version 1.0
// Author: Chat GPT-4.0 (Prompting and quality testing by Derick Schaefer)
// Date: 9/1/2024
// Description: This program calculates the value of Pi to a specified number 
//              of decimal places using the Gauss-Legendre Algorithm. It is 
//              designed to be accurate and can handle large numbers of digits 
//              with MPFR precision.
// ==========================================

// ==========================================================================
// Headers
// ==========================================================================

// MPFR Library for high-precision floating-point arithmetic
#include <mpfr.h>

// Standard Input/Output for displaying results and error messages
#include <stdio.h>

// Standard Library for memory allocation and process control
#include <stdlib.h>

// Math Library for advanced mathematical operations
#include <math.h>

// String Library for string comparison and manipulation
#include <string.h>  // For strcmp and strlen

// Unix Standard Library for handling delays (usleep)
#include <unistd.h>  // For usleep

// ==========================================================================
// Function Declarations
// ==========================================================================

/**
 * Function: calculate_pi
 * ----------------------
 * Calculates Pi to a specified number of decimal places using the Gauss-Legendre
 * Algorithm and the MPFR precision library.
 *
 * Parameters:
 *  pi      - A high-precision floating-point variable (mpfr_t) to store the result.
 *  digits  - The number of decimal places to calculate.
 *
 * This function uses multiple iterations to improve the precision of the result.
 * Each iteration involves calculating intermediate variables and refining their values.
 */
void calculate_pi(mpfr_t pi, unsigned long int digits) {
    
    // Set the precision based on the required digits (4 bits per decimal + extra for rounding precision)
    mpfr_prec_t precision = digits * 4 + 128;  // Extra bits to avoid rounding issues

    // ==================================================================
    // Variable Initialization
    // ==================================================================
    
    mpfr_t a, b, t, p, a_next, b_next, t_next, pi_approx;  // Intermediate variables for calculation

    // Initialize each variable with the required precision
    mpfr_init2(a, precision);
    mpfr_init2(b, precision);
    mpfr_init2(t, precision);
    mpfr_init2(p, precision);
    mpfr_init2(a_next, precision);
    mpfr_init2(b_next, precision);
    mpfr_init2(t_next, precision);
    mpfr_init2(pi_approx, precision);

    // ==================================================================
    // Initial Values
    // ==================================================================

    mpfr_set_ui(a, 1, MPFR_RNDN);        // a_0 = 1
    mpfr_sqrt_ui(b, 2, MPFR_RNDN);       // b_0 = sqrt(2)
    mpfr_ui_div(b, 1, b, MPFR_RNDN);     // b_0 = 1/sqrt(2)
    mpfr_set_ui(t, 1, MPFR_RNDN);        // t_0 = 1/4
    mpfr_div_ui(t, t, 4, MPFR_RNDN);
    mpfr_set_ui(p, 1, MPFR_RNDN);        // p_0 = 1

    // ==================================================================
    // Iterative Process
    // ==================================================================
    
    // Perform 10 iterations to improve precision (increase iterations for higher precision)
    for (int i = 0; i < 10; i++) {
        // Calculate the next 'a' value: a_next = (a + b) / 2
        mpfr_add(a_next, a, b, MPFR_RNDN);
        mpfr_div_ui(a_next, a_next, 2, MPFR_RNDN);

        // Calculate the next 'b' value: b_next = sqrt(a * b)
        mpfr_mul(b_next, a, b, MPFR_RNDN);
        mpfr_sqrt(b_next, b_next, MPFR_RNDN);

        // Calculate the next 't' value: t_next = t - p * (a - a_next)^2
        mpfr_sub(t_next, a, a_next, MPFR_RNDN);
        mpfr_pow_ui(t_next, t_next, 2, MPFR_RNDN);
        mpfr_mul(t_next, t_next, p, MPFR_RNDN);
        mpfr_sub(t_next, t, t_next, MPFR_RNDN);

        // Update 'p' for the next iteration: p = 2 * p
        mpfr_mul_ui(p, p, 2, MPFR_RNDN);

        // Update values for the next iteration
        mpfr_set(a, a_next, MPFR_RNDN);
        mpfr_set(b, b_next, MPFR_RNDN);
        mpfr_set(t, t_next, MPFR_RNDN);
    }

    // ==================================================================
    // Final Pi Calculation
    // ==================================================================
    
    // Calculate Pi using the formula: pi = (a + b)^2 / (4 * t)
    mpfr_add(pi_approx, a, b, MPFR_RNDN);  // pi_approx = a + b
    mpfr_pow_ui(pi_approx, pi_approx, 2, MPFR_RNDN);  // pi_approx = (a + b)^2
    mpfr_mul_ui(t, t, 4, MPFR_RNDN);  // t = 4 * t
    mpfr_div(pi, pi_approx, t, MPFR_RNDN);  // pi = (a + b)^2 / (4 * t)

    // ==================================================================
    // Cleanup
    // ==================================================================
    
    // Free memory allocated for MPFR variables
    mpfr_clear(a);
    mpfr_clear(b);
    mpfr_clear(t);
    mpfr_clear(p);
    mpfr_clear(a_next);
    mpfr_clear(b_next);
    mpfr_clear(t_next);
    mpfr_clear(pi_approx);
}

// ==========================================================================
// Main Function
// ==========================================================================

/**
 * Function: main
 * --------------
 * The entry point of the Pi calculation program. This function parses command-line 
 * arguments, initializes precision, calculates Pi using the `calculate_pi` function, 
 * and outputs the result with a delay between each digit.
 *
 * Parameters:
 *  argc   - Number of command-line arguments.
 *  argv   - Array of command-line arguments (including program name).
 *
 * Usage:
 *  <number_of_digits> <delay_in_milliseconds> [--no-round]
 *  - number_of_digits: Number of digits of Pi to calculate.
 *  - delay_in_milliseconds: Delay between printing each digit.
 *  - --no-round: Optional flag to prevent rounding of the last digit.
 *
 * Returns:
 *  0 on successful execution, 1 on invalid input or error.
 */
int main(int argc, char *argv[]) {

    // Check if the correct number of arguments is provided
    if (argc < 3 || argc > 4) {
        printf("Usage: %s <number_of_digits> <delay_in_milliseconds> [--no-round]\n", argv[0]);
        return 1;  // Exit with error code if usage is incorrect
    }

    // Parse command-line arguments
    int num_digits = atoi(argv[1]);          // Convert first argument to the number of digits
    int delay_ms = atoi(argv[2]);            // Convert second argument to delay in milliseconds
    int no_round = (argc == 4 && strcmp(argv[3], "--no-round") == 0);  // Check if --no-round flag is present

    // Initialize MPFR variable for Pi with higher precision
    mpfr_t pi;
    mpfr_init2(pi, num_digits * 4 + 128);  // Set precision to 4 bits per digit + extra bits

    // Call the function to calculate Pi with the given number of digits
    calculate_pi(pi, num_digits);

    // Allocate memory to store the Pi result as a string
    // Extra digits are added if --no-round is specified for truncation
    char *pi_str = malloc(num_digits + (no_round ? 11 : 3));  
    mpfr_sprintf(pi_str, "%.*Rf", num_digits + (no_round ? 10 : 0), pi);  // Convert Pi to string with desired precision

    // Truncate the result if --no-round flag is used (ignores rounding)
    if (no_round) {
        pi_str[num_digits + 2] = '\0';  // Truncate after the required number of digits
    }

    // ==================================================================
    // Output the result with a delay between each digit
    // ==================================================================
    
    for (int i = 0; i < strlen(pi_str); i++) {
        printf("%c", pi_str[i]);  // Print each digit of Pi
        fflush(stdout);           // Ensure immediate output
        usleep(delay_ms * 1000);  // Delay for the specified milliseconds
    }

    printf("\n");  // Print newline after all digits are displayed

    // ==================================================================
    // Cleanup
    // ==================================================================

    mpfr_clear(pi);  // Free MPFR memory used for Pi calculation
    free(pi_str);    // Free the memory allocated for the Pi string

    return 0;  // Return 0 to indicate successful execution
}

Based on the Gauss-Legendre algorithm.

Compile & Use

Click HERE for instructions setting up a Linux environment

gcc -o picalc picalc.c -lmpfr -lgmp
Usage: ./picalc <number_of_digits> <delay_in_milliseconds> [--no-round]

./picalc 50 500 --no-round

History & Inspiration

This calculator was inspired by an early 1980s program that ran on an Apple II computer, which took approximately 48 hours to calculate π to 100 digits of precision. While modern computing can now calculate billions of digits of π with ease, we added the <delay> parameter to this program to make the output more visually engaging. According to estimates by OpenAI, a delay input of 1,728,000 milliseconds will simulate the pace of that original Apple II’s π calculation!

AI Prompts

The following instructions were given to Chat-GPT:

  1. Create using the C programming language targeting Debian Linux based distributions like Ubuntu and Raspbian.
  2. The program should take two inputs. The first is for the number of digits of precision and the second is the delay in milliseconds between displaying each digit to the screen.
  3. The program should output individual digits of precision using Gauss-Legendre and at no point use scientific notation

This is what it understood:

1. Input Arguments:

• The program accepts two command-line arguments:

1. Number of Digits: The first argument specifies how many digits of π to calculate.

2. Delay: The second argument specifies the delay (in milliseconds) between printing each digit.

2. Precision Setting:

• The program sets the precision of the calculation based on the number of digits requested by the user. The precision needs to be high enough to accurately calculate the specified number of digits of π.

3. Calculation of π:

• The program calculates π using a series expansion method (such as the Gauss-Legendre). The calculation needs to continue until it can generate the required number of digits.

4. Output Formatting:

• After calculating π, the result is formatted as a string with the correct number of digits.

• The program outputs each digit of π one by one, with a delay between each digit as specified by the user.

5. Delay Mechanism:

• The delay between printing each digit is controlled by the user-defined number of milliseconds. This is implemented using a sleep function that pauses the program for the specified amount of time.

6. Error Handling:

• The program checks if the correct number of arguments is provided. If not, it outputs a usage message and exits.

• If invalid inputs (such as non-numeric arguments) are provided, the program should ideally handle these gracefully, although basic error handling was the primary focus.