Companion code · Chapter 5

Arrays, Strings, and Matrices: n, n², n³

6 files. About this chapter.

Download ch05.zip

ch05/agent_math.c41 lines
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
int stn_sqrt(double x, double *root, int *iters) {
    double guess = x / 2.0;
    *iters = 0;
    while (fabs(guess * guess - x) > 1e-10) {
        guess = (guess + x / guess) / 2.0;
        (*iters)++;
    }
    *root = guess;
    return 0;
}
int stn_factorial(unsigned n, unsigned long long *result) {
    long long f = 1;
    for (unsigned i = 2; i <= n; i++) {
        f *= i;
        if (f < 0) return -2;      /* "overflow detected" */
    }
    *result = (unsigned long long)f;
    return 0;
}
int stn_mean_sd(const double *v, size_t n, double *mean, double *sd) {
    double s = 0, s2 = 0;
    for (size_t i = 0; i < n; i++) { s += v[i]; s2 += v[i] * v[i]; }
    *mean = s / n;
    *sd = sqrt(s2 / n - *mean * *mean);
    return 0;
}
int main(int argc, char **argv) {
    const char *t = argc > 1 ? argv[1] : "";
    if (t[0] == 's') { double r; int it; int rc = stn_sqrt(atof(argv[2]), &r, &it); printf("stn_sqrt(%g) rc=%d iters=%d %.17g\n", atof(argv[2]), rc, it, r); }
    if (t[0] == 'f') { unsigned long long f = 0; int rc = stn_factorial((unsigned)atoi(argv[2]), &f); printf("stn_factorial(%d) rc=%d result=%llu\n", atoi(argv[2]), rc, f); }
    if (t[0] == 'd') {
        double r[3] = {1013.2, 1013.2, 1013.2}, r2[5] = {21.5, 21.6, 21.8, 22.0, 21.9}, m, sd; int rc;
        rc = stn_mean_sd(r, 3, &m, &sd); printf("equal rc=%d sd=%g\n", rc, sd);
        rc = stn_mean_sd(r2, 5, &m, &sd); printf("temps rc=%d sd=%g\n", rc, sd);
        rc = stn_mean_sd(r, 0, &m, &sd); printf("n=0 rc=%d mean=%g sd=%g\n", rc, m, sd);
    }
    return 0;
}
ch05/ids.txt9 lines
1001
  1004  
10o1

-1002
99999999999999999999999
2001x
42
1001111111111111111111111111111111111111
ch05/parse_id.c38 lines
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* Parses a whole line as a sensor ID in [1001, 9999].
   Returns 0 and sets *out on success, -1 on any error. */
static int parse_sensor_id(const char *s, long *out) {
    char *end;
    errno = 0;
    long v = strtol(s, &end, 10);
    if (end == s) return -1;            /* no digits      */
    if (errno == ERANGE) return -1;     /* too big        */
    while (*end == ' ' || *end == '\t') end++;
    if (*end != '\n' && *end != '\0') return -1; /* junk */
    if (v < 1001 || v > 9999) return -1; /* out of range */
    *out = v;
    return 0;
}

int main(void) {
    char line[32];
    while (fgets(line, sizeof line, stdin) != NULL) {
        if (strchr(line, '\n') == NULL && !feof(stdin)) {
            int c;
            while ((c = getchar()) != '\n' && c != EOF) { }
            printf("too long   -> rejected\n");
            continue;
        }
        long id;
        line[strcspn(line, "\n")] = '\0';
        if (parse_sensor_id(line, &id) == 0)
            printf("%-10s -> sensor %ld\n", line, id);
        else
            printf("%-10s -> rejected\n", line);
    }
    return 0;
}
ch05/stn_copy.c29 lines
#include <stdio.h>
#include <string.h>

/* Copies src into dst (capacity cap bytes, cap >= 1).
   Always terminates dst. Returns 0 if all of src fit,
   1 if it was truncated, -1 on bad arguments. */
int stn_copy(char *dst, size_t cap, const char *src) {
    if (dst == NULL || src == NULL || cap == 0) {
        return -1;
    }
    size_t len = strlen(src);
    if (len < cap) {
        memcpy(dst, src, len + 1);   /* + 1 copies the '\0' */
        return 0;
    }
    memcpy(dst, src, cap - 1);
    dst[cap - 1] = '\0';
    return 1;
}

int main(void) {
    const char *names[] = {"PRESSURE-03", "WIND-04", ""};
    char name[8];
    for (size_t i = 0; i < 3; i++) {
        int rc = stn_copy(name, sizeof name, names[i]);
        printf("rc = %d, name = \"%s\"\n", rc, name);
    }
    return 0;
}
ch05/timing.c86 lines
#define _POSIX_C_SOURCE 199309L
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

double stn_sum(const double *x, size_t n) {            /* n */
    double s = 0.0;
    for (size_t i = 0; i < n; i++) s += x[i];
    return s;
}
size_t stn_close_pairs(const double *x, size_t n, double tol) {
    size_t count = 0;                          /* n(n-1)/2 */
    for (size_t i = 0; i < n; i++)
        for (size_t j = i + 1; j < n; j++)
            if (fabs(x[i] - x[j]) < tol) count++;
    return count;
}
void stn_matmul(const double *a, const double *b, double *c,
                size_t n) {                         /* n*n*n */
    for (size_t i = 0; i < n; i++)
        for (size_t j = 0; j < n; j++) {
            double s = 0.0;
            for (size_t k = 0; k < n; k++)
                s += a[i*n + k] * b[k*n + j];
            c[i*n + j] = s;
        }
}

static double now_s(void) {
    struct timespec t;
    clock_gettime(CLOCK_MONOTONIC, &t);
    return (double)t.tv_sec + (double)t.tv_nsec * 1e-9;
}

/* Fill x with pseudo-random temperatures in [15, 25). */
static void fill(double *x, size_t n) {
    for (size_t i = 0; i < n; i++) x[i] = 15.0 + 10.0 * (double)rand() / ((double)RAND_MAX + 1.0);
}

/* Best of 5 runs, in milliseconds. */
#define BEST_OF(expr, out) do { double best_ = 1e30; \
    for (int r_ = 0; r_ < 5; r_++) { double t0_ = now_s(); expr; \
        double dt_ = now_s() - t0_; if (dt_ < best_) best_ = dt_; } \
    (out) = best_ * 1e3; } while (0)

/* Build with -DDIV=16 for a quick sanitizer check at small n. */
#ifndef DIV
#define DIV 1
#endif

static volatile double sink;   /* stops the optimizer deleting work */

int main(void) {
    srand(42);
    printf("%-14s %10s %10s %7s\n", "routine", "n", "ms", "ratio");
    double prev = 0.0, ms;
    for (size_t n = (1u << 21) / DIV; n <= (1u << 25) / DIV; n <<= 1) {
        double *x = malloc(n * sizeof *x);
        if (x == NULL) return 1;
        fill(x, n);
        BEST_OF(sink = stn_sum(x, n), ms);
        printf("%-14s %10zu %10.2f %7.2f\n", "sum (n)", n, ms, prev > 0 ? ms / prev : 0.0);
        prev = ms; free(x);
    }
    prev = 0.0;
    for (size_t n = 2000 / DIV; n <= 32000 / DIV; n <<= 1) {
        double *x = malloc(n * sizeof *x);
        if (x == NULL) return 1;
        fill(x, n);
        BEST_OF(sink = (double)stn_close_pairs(x, n, 0.01), ms);
        printf("%-14s %10zu %10.2f %7.2f\n", "pairs (n^2)", n, ms, prev > 0 ? ms / prev : 0.0);
        prev = ms; free(x);
    }
    prev = 0.0;
    for (size_t n = 128 / DIV; n <= 1024 / DIV; n <<= 1) {
        double *a = malloc(n * n * sizeof *a), *b = malloc(n * n * sizeof *b);
        double *c = malloc(n * n * sizeof *c);
        if (a == NULL || b == NULL || c == NULL) return 1;
        fill(a, n * n); fill(b, n * n);
        BEST_OF((stn_matmul(a, b, c, n), sink = c[n + 1]), ms);
        printf("%-14s %10zu %10.2f %7.2f\n", "matmul (n^3)", n, ms, prev > 0 ? ms / prev : 0.0);
        prev = ms; free(a); free(b); free(c);
    }
    return 0;
}
ch05/traverse.c33 lines
#define _POSIX_C_SOURCE 199309L
#include <stdio.h>
#include <time.h>

#ifndef N
#define N 4096
#endif
static double a[N * N];          /* 4096 x 4096 doubles = 128 MiB */

static double now_s(void) {
    struct timespec t;
    clock_gettime(CLOCK_MONOTONIC, &t);
    return (double)t.tv_sec + (double)t.tv_nsec * 1e-9;
}

int main(void) {
    for (size_t k = 0; k < (size_t)N * N; k++) a[k] = 1.0;

    double t0 = now_s(), s_row = 0.0;
    for (size_t i = 0; i < N; i++)          /* row by row: stride 8 B */
        for (size_t j = 0; j < N; j++)
            s_row += a[i * N + j];
    double t1 = now_s(), s_col = 0.0;
    for (size_t j = 0; j < N; j++)          /* column by column:      */
        for (size_t i = 0; i < N; i++)      /* stride 4096 * 8 bytes  */
            s_col += a[i * N + j];
    double t2 = now_s();

    printf("row-wise    sum %.0f in %6.1f ms\n", s_row, (t1 - t0) * 1e3);
    printf("column-wise sum %.0f in %6.1f ms\n", s_col, (t2 - t1) * 1e3);
    printf("ratio column/row = %.1f\n", (t2 - t1) / (t1 - t0));
    return 0;
}