Alright. So first, let me make it clear that the PRNG in libc
is deterministic (that's why it wants a seed), uses an LCG - meaning it's easy to predict all of the values once you have a few, and is therefore insecure.
Now then. erand48()
returns a random floating point value of double
size from a uniform distribution of pseudorandom real numbers. It doesn't take a seed value per se, but rather requires you to provide a state buffer. Here's the full declaration:
double erand48(unsigned short xsubi[3]);
The state buffer, amusingly enough, must be seeded by a random value in order for the generator to work. My first thought was to read from /dev/urandom
.
We can do that with something like this (using unbuffered reads to prevent wastage from such small reads):
#include <stdio.h>
#include <stdlib.h>
void *thread_f (void *i) {
// setup unbuffered urandom
urandom = fopen ("/dev/urandom", "r");
setvbuf (urandom, NULL, _IONBF, 0); // turn off buffering
// setup state buffer
unsigned short randstate[3];
// fgetc() returns a `char`, we need to fill a `short`
randstate[0] = (fgetc (urandom) << 8) | fgetc (urandom);
randstate[1] = (fgetc (urandom) << 8) | fgetc (urandom);
randstate[2] = (fgetc (urandom) << 8) | fgetc (urandom);
// cleanup urandom
fclose (urandom);
// you can now use erand48 (randstate);
... // do whatever work you need to do
return result;
}
This is thread safe, and even ensures a relatively safe seed value for all of the threads.
Of course, if speed isn't too much of an issue (ie: you can live with a small loss of speed) and you can live with integers, then doing unbuffered reads directly from /dev/urandom
is a perfect solution. Even better, /dev/urandom provides secure, unpredictable pseudorandom integers (well, technically, a byte stream, but they will always work as integers as long as you match the size) that are also usually uniformly distributed.
Plus, /dev/urandom
periodically has entropy injected into it and refreshed, ensuring that you have a decent supply of fairly random numbers.