I\'m trying to generate a random floating point number in between 0 and 1 (whether it\'s on [0,1] or [0,1) shouldn\'t matter for me). Every question online about this seems to i
OP has 2 issues:
How to started the sequence very randomly.
How to generate a double
on the [0...1) range.
The usual method is to take a very random source like /dev/urandom
or the result from the syscall()
or maybe even seed = time() ^ process_id;
and seed via srand()
. Then call rand()
as needed.
Below includes a quickly turned method to generate a uniform [0.0 to 1.0)
(linear distribution). But like all random generating functions, really good ones are base on extensive study. This one simply calls rand()
a few times based on DBL_MANT_DIG
and RAND_MAX
,
[Edit] Original double rand_01(void)
has a weakness in that it only generates a 2^52 different double
s rather than 2^53. It has been amended. Alternative: a double
version of rand_01_ld(void)
far below.
#include
#include
#include
#include
#include
#include
#include
double rand_01(void) {
assert(FLT_RADIX == 2); // needed for DBL_MANT_DIG
unsigned long long limit = (1ull << DBL_MANT_DIG) - 1;
double r = 0.0;
do {
r += rand();
// Assume RAND_MAX is a power-of-2 - 1
r /= (RAND_MAX/2 + 1)*2.0;
limit = limit / (RAND_MAX/2 + 1) / 2;
} while (limit);
// Use only DBL_MANT_DIG (53) bits of precision.
if (r < 0.5) {
volatile double sum = 0.5 + r;
r = sum - 0.5;
}
return r;
}
int main(void) {
FILE *istream = fopen("/dev/urandom", "rb");
assert(istream);
unsigned long seed = 0;
for (unsigned i = 0; i < sizeof seed; i++) {
seed *= (UCHAR_MAX + 1);
int ch = fgetc(istream);
assert(ch != EOF);
seed += (unsigned) ch;
}
fclose(istream);
srand(seed);
for (int i=0; i<20; i++) {
printf("%f\n", rand_01());
}
return 0;
}
If one wanted to extend to an even wider FP, unsigned wide integer types may be insufficient. Below is a portable method that does not have that limitation.
long double rand_01_ld(void) {
// These should be calculated once rather than each function call
// Leave that as a separate implementation problem
// Assume RAND_MAX is power-of-2 - 1
assert((RAND_MAX & (RAND_MAX + 1U)) == 0);
double rand_max_p1 = (RAND_MAX/2 + 1)*2.0;
unsigned BitsPerRand = (unsigned) round(log2(rand_max_p1));
assert(FLT_RADIX != 10);
unsigned BitsPerFP = (unsigned) round(log2(FLT_RADIX)*LDBL_MANT_DIG);
long double r = 0.0;
unsigned i;
for (i = BitsPerFP; i >= BitsPerRand; i -= BitsPerRand) {
r += rand();
r /= rand_max_p1;
}
if (i) {
r += rand() % (1 << i);
r /= 1 << i;
}
return r;
}