The standard C function is `rand()`. It's good enough to deal cards for solitaire, but it's awful. Many implementations of `rand()` cycle through a short list of numbers, and the low bits have shorter cycles. The way that some programs call `rand()` is awful, and calculating a good seed to pass to `srand()` is hard.
The best way to generate random numbers in C is to use a third-party library like OpenSSL. For example,
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <openssl/rand.h>
/* Random integer in [0, limit) */
unsigned int random_uint(unsigned int limit) {
union {
unsigned int i;
unsigned char c[sizeof(unsigned int)];
} u;
do {
if (!RAND_bytes(u.c, sizeof(u.c))) {
fprintf(stderr, "Can't get random bytes!\n");
exit(1);
}
} while (u.i < (-limit % limit)); /* u.i < (2**size % limit) */
return u.i % limit;
}
/* Random double in [0.0, 1.0) */
double random_double() {
union {
uint64_t i;
unsigned char c[sizeof(uint64_t)];
} u;
if (!RAND_bytes(u.c, sizeof(u.c))) {
fprintf(stderr, "Can't get random bytes!\n");
exit(1);
}
/* 53 bits / 2**53 */
return (u.i >> 11) * (1.0/9007199254740992.0);
}
int main() {
printf("Dice: %d\n", (int)(random_uint(6) + 1));
printf("Double: %f\n", random_double());
return 0;
}
Why so much code? Other languages like Java and Ruby have functions for random integers or floats. OpenSSL only gives random bytes, so I try to mimic how Java or Ruby would transform them into integers or floats.
For integers, we want to avoid *modulo bias*. Suppose that we got some random 4 digit integers from `rand() % 10000`, but `rand()` can only return 0 to 32767 (as it does in Microsoft Windows). Each number from 0 to 2767 would appear more often than each number from 2768 to 9999. To remove the bias, we can retry `rand()` while the value is below 2768, because the 30000 values from 2768 to 32767 map uniformly onto the 10000 values from 0 to 9999.
For floats, we want 53 random bits, because a `double` holds 53 bits of precision (assuming it's an IEEE double). If we use more than 53 bits, we get rounding bias. Some programmers write code like `rand() / (double)RAND_MAX`, but `rand()` might return only 31 bits, or only 15 bits in Windows.
OpenSSL's `RAND_bytes()` seeds itself, perhaps by reading `/dev/urandom` in Linux. If we need many random numbers, it would be too slow to read them all from `/dev/urandom`, because they must be copied from the kernel. It is faster to allow OpenSSL to generate more random numbers from a seed.
More about random numbers:
- [Perl's Perl_seed()][1] is an example of how to calculate a seed in C for `srand()`. It mixes bits from the current time, the process ID, and some pointers, if it can't read `/dev/urandom`.
- [OpenBSD's arc4random_uniform()][2] explains modulo bias.
- [Java API for java.util.Random][3] describes algorithms for removing bias from random integers, and packing 53 bits into random floats.
[1]:
[To see links please register here]
[2]:
[To see links please register here]
[3]:
[To see links please register here]