forked from 0ad/0ad
63 lines
1.7 KiB
C++
63 lines
1.7 KiB
C++
|
/**
|
||
|
* =========================================================================
|
||
|
* File : rand.cpp
|
||
|
* Project : 0 A.D.
|
||
|
* Description : pseudorandom number generator
|
||
|
* =========================================================================
|
||
|
*/
|
||
|
|
||
|
// license: GPL; see lib/license.txt
|
||
|
|
||
|
#include "precompiled.h"
|
||
|
#include "rand.h"
|
||
|
|
||
|
// avoids several common pitfalls; see discussion at
|
||
|
// http://www.azillionmonkeys.com/qed/random.html
|
||
|
|
||
|
// rand() is poorly implemented (e.g. in VC7) and only returns < 16 bits;
|
||
|
// double that amount by concatenating 2 random numbers.
|
||
|
// this is not to fix poor rand() randomness - the number returned will be
|
||
|
// folded down to a much smaller interval anyway. instead, a larger XRAND_MAX
|
||
|
// decreases the probability of having to repeat the loop.
|
||
|
#if RAND_MAX < 65536
|
||
|
static const uint XRAND_MAX = (RAND_MAX+1)*(RAND_MAX+1) - 1;
|
||
|
static uint xrand()
|
||
|
{
|
||
|
return rand()*(RAND_MAX+1) + rand();
|
||
|
}
|
||
|
// rand() is already ok; no need to do anything.
|
||
|
#else
|
||
|
static const uint XRAND_MAX = RAND_MAX;
|
||
|
static uint xrand()
|
||
|
{
|
||
|
return rand();
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
uint rand(uint min_inclusive, uint max_exclusive)
|
||
|
{
|
||
|
const uint range = (max_exclusive-min_inclusive);
|
||
|
// huge interval or min >= max
|
||
|
if(range == 0 || range > XRAND_MAX)
|
||
|
{
|
||
|
WARN_ERR(ERR::INVALID_PARAM);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
const uint inv_range = XRAND_MAX / range;
|
||
|
|
||
|
// generate random number in [0, range)
|
||
|
// idea: avoid skewed distributions when <range> doesn't evenly divide
|
||
|
// XRAND_MAX by simply discarding values in the "remainder".
|
||
|
// not expected to run often since XRAND_MAX is large.
|
||
|
uint x;
|
||
|
do
|
||
|
x = xrand();
|
||
|
while(x >= range * inv_range);
|
||
|
x /= inv_range;
|
||
|
|
||
|
x += min_inclusive;
|
||
|
debug_assert(x < max_exclusive);
|
||
|
return x;
|
||
|
}
|