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:
#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.0Run and edit it in the app.
floorrounds down,ceilrounds up,roundgoes 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 themath.hone. Usingabson a double quietly chops off the fraction.- On Linux, add
-lmwhen compiling by hand:gcc main.c -o main -lm.
Distance between two points
Pythagoras, with sqrt:
#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.41Run 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:
#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
1Run 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:
#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 astruct tmwith fields liketm_year,tm_mon,tm_mday,tm_hour.strftime— formats it as text.clock()— processor time used so far; divide byCLOCKS_PER_SECfor seconds.
#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; mostlydouble.- Pseudo-random — Numbers from a formula that look random but repeat for the same seed.
- Seed (
srand) — The starting point forrand'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?