PI Digits Calculator

PI Digits is a versatile calculator designed to generate π outputs that can be easily used in tables or integrated into other programs. It offers a default, reader-friendly format suitable for BASH, as well as options for CSV and JSON outputs. Written in C using the Gauss-Legendre algorithm, this tool was created by OpenAI’s GPT-4.0 and targets Debian-based Linux distributions, including Ubuntu and Raspbian.

Code

// ==========================================
// Pi Calculation Program - Version 1.0
// Author: ChatGPT-4.0 (Prompting and quality testing by Derick Schaefer)
// Date: 8/15/2024
// Description: This program calculates Pi to a specified number of digits 
//              using the Gauss-Legendre Algorithm. It supports multiple 
//              output formats (table, CSV, JSON) and allows for truncation 
//              of digits without rounding via the --no-round option. It 
//              utilizes the MPFR library for high-precision arithmetic.
// ==========================================

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

#include <mpfr.h>    // MPFR Library for high-precision floating-point arithmetic
#include <stdio.h>   // Standard input/output functions
#include <stdlib.h>  // Memory management, atoi()
#include <string.h>  // String manipulation functions (e.g., strcmp, strlen)

// ==========================================================================
// Function: calculate_pi
// Description: Calculates Pi using the Gauss-Legendre Algorithm to the 
//              specified number of digits of precision.
// Parameters:
//    - pi: mpfr_t variable to store the calculated Pi value.
//    - digits: The number of digits of Pi to calculate.
// Notes: This function performs an iterative process to approximate Pi.
//        Extra precision bits are used to avoid rounding errors.
// ==========================================================================
void calculate_pi(mpfr_t pi, unsigned long int digits) {
    // Set precision based on the required number of digits, with extra bits for accuracy
    mpfr_prec_t precision = digits * 4 + 128;  // Extra bits to avoid rounding issues

    // Initialize MPFR variables with the calculated precision
    mpfr_t a, b, t, p, a_next, b_next, t_next, pi_approx;
    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 for the Gauss-Legendre algorithm
    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);      // Divide t_0 by 4
    mpfr_set_ui(p, 1, MPFR_RNDN);         // p_0 = 1

    // =======================================================================
    // Iterative Process
    // =======================================================================
    for (int i = 0; i < 10; i++) {  // 10 iterations for reasonable precision
        // a_next = (a + b) / 2
        mpfr_add(a_next, a, b, MPFR_RNDN);
        mpfr_div_ui(a_next, a_next, 2, MPFR_RNDN);

        // b_next = sqrt(a * b)
        mpfr_mul(b_next, a, b, MPFR_RNDN);
        mpfr_sqrt(b_next, b_next, MPFR_RNDN);

        // 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);

        // p_next = 2 * p
        mpfr_mul_ui(p, p, 2, MPFR_RNDN);

        // Update variables 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 Calculation of Pi
    // =======================================================================
    // Pi approximation: pi = (a + b)^2 / (4 * t)
    mpfr_add(pi_approx, a, b, MPFR_RNDN);      // (a + b)
    mpfr_pow_ui(pi_approx, pi_approx, 2, MPFR_RNDN);  // (a + b)^2
    mpfr_mul_ui(t, t, 4, MPFR_RNDN);           // 4 * t
    mpfr_div(pi, pi_approx, t, MPFR_RNDN);     // pi = (a + b)^2 / (4 * t)

    // Clear all MPFR variables to free memory
    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);
}

// ==========================================================================
// Output Functions
// Description: These functions format and output Pi in different formats: 
//              table, CSV, and JSON.
// Parameters:
//    - pi_str: The string representation of Pi.
// ==========================================================================

// Outputs Pi in table format
void print_table_format(char *pi_str) {
    printf("Digit | Value\n");
    printf("--------------\n");
    for (int i = 0; i < strlen(pi_str); i++) {
        printf("%5d | %c\n", i + 1, pi_str[i]);
    }
}

// Outputs Pi in CSV format
void print_csv_format(char *pi_str) {
    printf("Digit,Value\n");
    for (int i = 0; i < strlen(pi_str); i++) {
        printf("%d,%c\n", i + 1, pi_str[i]);
    }
}

// Outputs Pi in JSON format
void print_json_format(char *pi_str) {
    printf("{\n  \"pi_digits\": [\n");
    for (int i = 0; i < strlen(pi_str); i++) {
        printf("    {\"Digit\": %d, \"Value\": \"%c\"}%s\n", i + 1, pi_str[i], (i + 1 < strlen(pi_str)) ? "," : "");
    }
    printf("  ]\n}\n");
}

// ==========================================================================
// Main Program
// Description: This program calculates Pi to a specified number of digits, 
//              with options for output format and truncation without rounding.
// Parameters:
//    - argv[1]: Number of digits of Pi to calculate.
//    - argv[2]: Optional format (table, csv, json).
//    - argv[3]: Optional --no-round switch to disable rounding.
// ==========================================================================
int main(int argc, char *argv[]) {
    // Ensure correct number of arguments
    if (argc < 2 || argc > 4) {
        printf("Usage: %s <number_of_digits> [--format=<table|csv|json>] [--no-round]\n", argv[0]);
        return 1;
    }

    // Parse number of digits and options
    int num_digits = atoi(argv[1]);
    int no_round = 0;           // Default is rounding
    char *format = "table";      // Default format is table

    // Parse command-line options
    for (int i = 2; i < argc; i++) {
        if (strncmp(argv[i], "--format=", 9) == 0) {
            format = argv[i] + 9;  // Extract the format string
        } else if (strcmp(argv[i], "--no-round") == 0) {
            no_round = 1;  // Enable no-rounding
        } else {
            printf("Error: Invalid option '%s'.\n", argv[i]);
            return 1;
        }
    }

    // =======================================================================
    // Pi Calculation
    // =======================================================================
    mpfr_t pi;
    mpfr_init2(pi, num_digits * 4 + 128);  // Set MPFR precision

    // Calculate Pi to the specified number of digits
    calculate_pi(pi, num_digits);

    // Convert Pi to a string with or without rounding
    char *pi_str = malloc(num_digits + (no_round ? 11 : 3));
    mpfr_sprintf(pi_str, "%.*Rf", num_digits + (no_round ? 10 : 0), pi);

    // Truncate extra digits if no-round option is used
    if (no_round) {
        pi_str[num_digits + 2] = '\0';
    }

    // =======================================================================
    // Output
    // =======================================================================
    // Output Pi in the requested format
    if (strcmp(format, "table") == 0) {
        print_table_format(pi_str);
    } else if (strcmp(format, "csv") == 0) {
        print_csv_format(pi_str);
    } else if (strcmp(format, "json") == 0) {
        print_json_format(pi_str);
    } else {
        printf("Error: Unknown format '%s'. Supported formats are table, csv, and json.\n", format);
        free(pi_str);
        mpfr_clear(pi);
        return 1;
    }

    // =======================================================================
    // Cleanup
    // =======================================================================
    // Free memory and clear MPFR variables
    free(pi_str);
    mpfr_clear(pi);

    return 0;
}

Compile & Use

gcc -o pidig pidig.c -lmpfr -lgmp
Usage: ./pidig <number_of_digits> [--format=<table|csv|json>] [--no-round]

./pidig 7 --format=table --no-round

Click HERE for instructions setting up a Linux environment

Digit | Value
--------------
    1 | 3
    2 | .
    3 | 1
    4 | 4
    5 | 1
    6 | 5
    7 | 9

{
  "pi_digits": [
    {"Digit": 1, "Value": "3"},
    {"Digit": 2, "Value": "."},
    {"Digit": 3, "Value": "1"},
    {"Digit": 4, "Value": "4"},
    {"Digit": 5, "Value": "1"},
    {"Digit": 6, "Value": "5"},
    {"Digit": 7, "Value": "9"}
  ]
}

    
Digit,Value
1,3
2,.
3,1
4,4
5,1
6,5
7,9

AI Prompts

The following instructions were given to Chat-GPT:

  1. This be a C application targeting Debian Linux (Ubuntu, Raspbian). It will use the GMP library for precision.
  2. The PI calculation method should be Gauss-Legendre
  3. The output will focus on a digit by digit approach creating pairs
  4. The default output will be by creating a table. You can use ASCII – and | for the table
  5. A –format= switch will be used to specify table (default), CSV, and JSON
  6. The last requirement is the program will have 1 input which is the number of digits of precision to calculate PI to
  7. Can you repeat to me what you understand about my requirements?

This is what it understood:

  1. Platform: The program will be a C application targeting Debian Linux (Ubuntu, Raspbian) and will use the GMP library for precision.
  2. Calculation Method: The program will use the Gauss-Legendre algorithm to calculate the value of PI.
  3. Output Format: The program will focus on a digit-by-digit output, generating <digit> <value> pairs.
  4. Default Output: The default output will be in the form of a table, using ASCII – and | characters for the table structure.
  5. Format Switch: The program will include a –format= switch to specify the output format, which can be table (default), CSV, or JSON.
  6. Input: The program will have a single input, which is the number of digits of precision for calculating PI.

The Struggle

ChatGPT faced significant challenges while working on this program. Some of the issues included:

• Inserting an extra “.” between the digits “3” and “14,” or omitting it entirely.

• Incorrectly formatting the table and misnumbering the digits.

Even after addressing these formatting issues, the program struggled with the mathematical implementation. Despite clear instructions to use the Gauss-Legendre algorithm, it repeatedly attempted to apply the Chudnovsky algorithm instead, leading to segmentation faults after numerous attempts to correct the issue. Eventually it was given Simple C’s source code to try and get this correct.

In total, this program took over an hour to complete. This highlights a key challenge: while an LLM can excel at certain tasks, it may struggle with the finer details of implementation.