Skip to content
FollowtheDamnCode

Math, Random Numbers, and Time

math.h, rand and srand, and the clock.

7 minute read

Big idea

Three small libraries do a lot of everyday work: <math.h> for square roots, powers and rounding; rand from <stdlib.h> for random numbers; and <time.h> for the clock.

math.h

Most math functions take and return double:

The math.h functions you'll use most
#include <stdio.h>
#include <math.h>

int main(void) {
    printf("sqrt(16)    = %.1f\n", sqrt(16));
    printf("pow(2, 10)  = %.0f\n", pow(2, 10));
    printf("fabs(-3.5)  = %.1f\n", fabs(-3.5));
    printf("floor(2.7)  = %.1f\n", floor(2.7));
    printf("ceil(2.1)   = %.1f\n", ceil(2.1));
    printf("round(2.5)  = %.1f\n", round(2.5));
    printf("hypot(3, 4) = %.1f\n", hypot(3, 4));
    return 0;
}
output
sqrt(16)    = 4.0
pow(2, 10)  = 1024
fabs(-3.5)  = 3.5
floor(2.7)  = 2.0
ceil(2.1)   = 3.0
round(2.5)  = 3.0
hypot(3, 4) = 5.0

Run and edit it in the app.

  • floor rounds down, ceil rounds up, round goes to the nearest (halves away from zero).
  • They return double, so print with %f — or convert: int n = (int) floor(x);.
  • abs(int) lives in <stdlib.h>; fabs(double) is the math.h one. Using abs on a double quietly chops off the fraction.
  • On Linux, add -lm when compiling by hand: gcc main.c -o main -lm.

Distance between two points

Pythagoras, with sqrt:

How far apart are (1, 2) and (4, 6)?
#include <stdio.h>
#include <math.h>

double distance(double x1, double y1, double x2, double y2) {
    double dx = x2 - x1;
    double dy = y2 - y1;
    return sqrt(dx * dx + dy * dy);
}

int main(void) {
    printf("%.2f\n", distance(1, 2, 4, 6));
    printf("%.2f\n", distance(0, 0, 1, 1));
    return 0;
}
output
5.00
1.41

Run and edit it in the app.

Comparing doubles

Decimals aren't stored exactly, so 0.1 + 0.2 == 0.3 is false. Compare with a small tolerance instead:

Predict: are they equal?
#include <stdio.h>
#include <math.h>

int main(void) {
    double a = 0.1 + 0.2;
    printf("%d\n", a == 0.3);
    printf("%d\n", fabs(a - 0.3) < 1e-9);
    return 0;
}
What does it print? Show the answer
0
1

Run and edit it in the app.

Random numbers

rand() (from <stdlib.h>) returns a whole number from 0 up to RAND_MAX. To get a number in a range, use %:

int die = rand() % 6 + 1;          // 1 to 6
int pct = rand() % 101;            // 0 to 100
int n = rand() % (hi - lo + 1) + lo;   // lo to hi

rand isn't truly random — it's a pseudo-random sequence that starts from a seed. Same seed, same sequence. Seed it once, with the current time, to get a different sequence each run:

Roll five dice — run it twice
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main(void) {
    srand((unsigned) time(NULL));   // seed once, at the start
    for (int i = 0; i < 5; i++) {
        printf("%d ", rand() % 6 + 1);
    }
    printf("\n");
    return 0;
}

Run and edit it in the app.

Change it to srand(42) and every run prints the same five rolls — useful when you're testing and want repeatable results.

rand() is fine for games and practice. It is not fine for passwords, keys, or anything secret.

Watch it

time.h: the clock

  • time(NULL) — the current time as a number of seconds (since 1 January 1970).
  • localtime(&t) — breaks it into a struct tm with fields like tm_year, tm_mon, tm_mday, tm_hour.
  • strftime — formats it as text.
  • clock() — processor time used so far; divide by CLOCKS_PER_SEC for seconds.
Today's date, and how long a loop took
#include <stdio.h>
#include <time.h>

int main(void) {
    time_t now = time(NULL);
    struct tm *t = localtime(&now);
    char text[64];
    strftime(text, sizeof text, "%Y-%m-%d %H:%M", t);
    printf("Now: %s\n", text);

    clock_t start = clock();
    volatile long sum = 0;
    for (long i = 0; i < 20000000; i++) sum += i;
    double secs = (double) (clock() - start) / CLOCKS_PER_SEC;
    printf("Loop took %.3f s\n", secs);
    return 0;
}

Run and edit it in the app.

Watch out for two struct tm quirks: tm_year counts years since 1900, and tm_mon runs 0 to 11. strftime handles both for you.

Which expression gives a random whole number from 10 to 20, inclusive?

Common mistake

Calling srand(time(NULL)) inside a loop. time only changes once a second, so you keep re-seeding with the same value and get the same "random" number over and over. Seed once, at the top of main.

Think of it like

rand is a very long shuffled deck of numbers. The seed decides where in the deck you start dealing. Start at the same place and you'll deal the same cards; start somewhere based on the clock and every game is different.

Key terms

  • <math.h> — sqrt, pow, fabs, floor, ceil, round, trig functions; mostly double.
  • Pseudo-random — Numbers from a formula that look random but repeat for the same seed.
  • Seed (srand) — The starting point for rand's sequence.
  • time(NULL) — The current time in seconds.
  • struct tm — A time broken into year, month, day, hour, and so on.

Check yourself

What does floor(-2.5) return?

Why call srand(time(NULL)) once at the start?

How should you check whether two doubles a and b are equal?