This answer mostly applies to people looking for random doubles on x86_64 machines.
Being a long time C user (since late 1980s), I gave up caring what the RAND_MAX value of the day is.
Also, the srand(time(NULL)
indicates to me that the numbers are generated with some quasi random number generator of (at least to me) unknown quality. And all that, while you are just 1 assembly instruction away from CPU random numbers on modern x86_64 machines.
So, the code below uses rdrand
via intrinsics, which is known to be a full 64bit random number as a source of randomness. This way, at least, you have sufficient bits to generate a double without further ado. If - instead - you opted for C library rand()
and it returned a 32 bit value, you might have not enough bits for a 64 floating point number. And there is no randl(), randul()
or alike in Ansi C, afaik.
But - if you look at the signature of the _rdrand_step()
intrinsic, it seems like this instruction might fail under certain conditions. (Load related, some say). So, in the code below, it might (or might not) be a good idea to write a while() loop or something like that around the intrinsic call.
#include <stdio.h>
#include <stdint.h>
#include <immintrin.h>
#include <float.h>
int randomf64(double minVal, double maxVal, double* out) {
if (NULL == out)
return 0;
uint64_t result = 0ULL;
// cast in next line works for amd64 (x86_64) on linux at least.
int rc = _rdrand64_step((unsigned long long*)&result);
if(rc) {
double unscaled = (double)result/(double)UINT64_MAX;
*out = minVal + (maxVal - minVal) * unscaled;
return 1;
}
return 0;
}
int main(int argc, const char* argv[]) {
size_t nvals = 1;
if(argc > 1) {
nvals = atol(argv[1]);
}
// We want to see if all that "can fail under stress" thing happens...
double *values = malloc(nvals * sizeof(double));
if (NULL != values) {
for(size_t i = 0; i < nvals; ++i ) {
if(!randomf64(-100.0,100.0, &values[i])) {
printf("boom! after %lu random numbers generated.\n",
i);
free(values);
exit(-1);
}
}
for(size_t i = 0; i < nvals; ++i) {
int Digs = DECIMAL_DIG;
printf("%lu %.*e\n", i, Digs, values[i]);
}
free(values);
}
return 0;
}
If you supply an integer as a command line argument, it generates a respective
number of random doubles and stores them in a heap allocated array.
This allows for testing if that "sporadic failing" might happen. I tried several times with up to 1E6 values created in a burst and it never failed (on some cheap AMD CPU).
In order to compile this, e.g. with clang, I used:
clang -mrdrnd -O3 -std=c17 -o r64i r64intrin.c
Please note, that you have to enable the usage of the intrinsic with -mrdrnd
for the compiler to be happy.