Some considerations about random numbers generation. GReaper is right pointing to the fact that the linear congruential random number generator is problematic. The lower bits tends to be less random. Notice that this is a characteristic of this specific PRNG and the method using floating-point is a way to mitigate the problem obtaining a value between 0.0 and 1.0, and multiplying by the desired range. But there is a faster way: Use the upper bits instead.
This expression:
Code:
int index = (int)((rand()/(double)RAND_MAX)*48 + 0.5);
Could be writen using, entirely, integer operations like:
Code:
int index = (rand() >> 25) % 50;
Why 25 bits right shift? Because we need values between 0 and 2⁶ (64, the upper 6 bits -- more bits will be less random!), but rand() always returns an unsigned value (it is usual RAND_MAX to have the most significant bit zeroed). And for i386 and x86-64 modes it is usual RAND_MAX to be 31 bits long...
The second function is faster the the first (no floating point required and just one division)...
Hardware RNG:
There is a faster way, and more random!
If you are using Intel or AMD processors and want a "truly random" number (not "pseudo"), Modern architectures (since Haswell, for instance) has an internal diode to measure "noise" caused by temperature. Using this "quantum effect" we have a "true" RNG (almost!). And there is an instruction to get these random values generated by the CPU: RDRAND.
There are two advantages: RDRAND is faster then rand() and will return 16, 32 or 64 bits of truly random number (not 31).
Except by the intrinsic function __cpuid(), I prefer to implement RDRAND in assembly (there is an instrinsic __randNN_step() available in immintrin.h). NN is 16, 32 or 64:
Code:
#include <stdlib.h>
#include <time.h>
#if defined( __x86_64__ ) || defined( __i386__ )
#include <cpuid.h>
static int supports_rdrand = 0;
// This will be called before main()...
__attribute__ ( ( constructor ) )
void test_rdrand_support ( void )
{
# ifdef __x86_64__
unsigned long long flags;
# else
unsigned int flags;
# endif
unsigned int a, b, c, d;
__asm__ __volatile__ (
# ifdef __x86_64__
"pushfq; popq %0"
# else
"pushfl; popl %0"
# endif
: "=g" ( flags ) );
// supports cpuid?
supports_rand = 0;
if ( flags & ( 1U << 21 ) )
supports_rdrand = 1;
if ( supports_rdrand )
{
__cpuid ( 1, a, b, c, d );
supports_rdrand = !! ( c & ( 1U << 30 ) );
if ( supports_rand )
return;
}
srand ( time ( NULL ) );
}
int getrand ( void )
{
if ( supports_rdrand )
{
int rnd;
__asm__ __volatile__ (
"1: rdrand %0; jnc 1b" : "=r" ( rnd )
);
return rnd;
}
else
return rand();
}
#else
# define getrand(...) rand()
#endif