The Madhava-Leibniz approximation to Pi

Serial version, and some timing macros, are included here.

Madhava-Leibniz-serial.c

/*
| Pi approximation, based on the Madhava-Leibnitz approximation
| 2020-10-12
*/
#include <stdio.h>
#include <stdlib.h> // strtod(), atoi()
#include "timing.h" // TIMING_initTime(), TIMING_reporttime()
#include <math.h>   // log10()

#include <unistd.h> // gethostname()
#define MAXNAME 256 // should be enough...

#define LABEL "Serial"

// long double only suppports 35 digits
#define PI35 3.1415926535897932384626433832795029L
//           -.---|----|----|----|----|----|----|

long double sum = 0.0L;

void terms(long double start, long double stop)
{
    long double factor = ((unsigned long)(start - 1) % 4) ? -1.0L : 1.0L ;
    for (long double x = start; x < stop; x += 2) {
        sum += factor * 1.0L / x;
        factor = -factor;
    }
}


int main(int argc, char **argv)
{
    char hostname[MAXNAME];
    gethostname(hostname, MAXNAME);
    hostname[MAXNAME-1] = '\0';     // guarantee a terminating null character

    TIMING_initTime(0, hostname, 0);

    // printf("Starting...\n");

    unsigned nthreads;
    long double nterms;
    if (argc == 3) {
        nthreads = atoi(argv[1]);
        nterms = strtold(argv[2], NULL);
    } else {
        fprintf(stderr, "usage: %s <nthreads> <nterms>\n", argv[0]);
        return 1;
    }

    long double first_term = 1.0L, last_term = 2.0 * nterms;

    //------------------------------------------------------------------
    // Sum up the terms:
    terms(first_term, last_term);

    // Calculate the approximation:
    long double pi_approx = 4.0L * sum;

    //------------------------------------------------------------------
    // Report the calculation results:
    long double error = pi_approx - PI35;
    if (error < 0.0L)
        error = -error;
    int width = abs(log10(error)) + 1;  // show this many digits...
    printf("#---- %s -- %Lg terms, %i threads ----\n",
        LABEL, nterms, nthreads);
    printf("pi35bits: %.35Lf\n  approx: %.*Lf\nerror=%.6Le\n#--------\n",
        PI35,  width, pi_approx,  error);

    TIMING_reportTime("Serial run", 0);

    return 0;
}

timing.h

/*
* High-resolution timing def's.
*
*   NCLOCKS
*   - # of TIMING_WALL structs (default 2)
*   - Define this to a larger (even) number before #including this header, if desired
*
*   TIMING_initTime( index, hoststring_str, hostrank_int )
*   - create a format string that includes hoststring_str and hostrank_int;
*   - start a timer
*
*   TIMING_reportTime( label_string, index )
*   - report elapsed time
*     - label_string - arbitrary text used in labeling
*     - index - associates this report with a "TIMING_initTime()" call
*
*   TIMING_check_resolution(string) -   display the claimed timing resolution
*     - string - label used in the display
*
* 2020-08-29
*/
#ifndef __TIMING__
#define __TIMING__
#include <time.h>   // clock_gettime(), clock()
#include <stdio.h>

#ifndef NCLOCKS
#   define NCLOCKS 2
#endif

// globally-available variables/arrays:
typedef struct {
    struct timespec start;
    struct timespec elapsed;
    struct timespec deltat;
    char fmt_string[1000];
} TIMING_WALL;
TIMING_WALL TIMING_clocks[NCLOCKS];

//------------------------------------------------------------------
// Set the starting time with sub-second precision...
#define TIMING_initTime(index, myhost, myrank) \
{ \
    sprintf(TIMING_clocks[index].fmt_string, \
        "%%s %s:%02i  wallclock: %%lu.%%09ld seconds", \
        (myhost), (myrank)); \
    clock_gettime(CLOCK_REALTIME, &(TIMING_clocks[index].start)); \
}

//------------------------------------------------------------------
// Report the time elapsed since the "initTime()"
#define TIMING_reportTime(lbl, index) \
{ \
    clock_gettime(CLOCK_REALTIME, &(TIMING_clocks[index].elapsed)); \
    TIMING_clocks[index].deltat.tv_sec = \
        TIMING_clocks[index].elapsed.tv_sec \
        - TIMING_clocks[index].start.tv_sec; \
    TIMING_clocks[index].deltat.tv_nsec = \
        TIMING_clocks[index].elapsed.tv_nsec \
        - TIMING_clocks[index].start.tv_nsec; \
    if (TIMING_clocks[index].deltat.tv_nsec < 0) { \
        TIMING_clocks[index].deltat.tv_sec--; \
        TIMING_clocks[index].deltat.tv_nsec += 1000000000L ; \
    } \
    printf(TIMING_clocks[index].fmt_string, (lbl), \
        TIMING_clocks[index].deltat.tv_sec, \
        TIMING_clocks[index].deltat.tv_nsec); \
    long double nsec = 1e9 * (TIMING_clocks[index].deltat.tv_sec) \
        + (TIMING_clocks[index].deltat.tv_nsec); \
    if (nsec > 1e9) \
        printf("\n"); \
    else if (nsec > 1e6) \
        printf(" (%Lf ms)\n", 1e-6*nsec); \
    else if (nsec > 1e3) \
        printf(" (%Lf us)\n", 1e-3*nsec); \
    else \
        printf(" (%Lf ns)\n", nsec); \
}

#define TIMING_check_resolution(lbl) \
{ \
    struct timespec res; \
    clock_getres(CLOCK_REALTIME, &res); \
    fprintf(stderr, "%s - Time resolution: %ld ns\n", \
        (lbl), res.tv_nsec); \
}

#else
extern TIMING_WALL *TIMING_clocks;
#endif