Explorations on Random Numbers, Part 2.5
filed in Code on Jul.28, 2011
Before we delve into the nitty-gritty details of how the compiler optimizes our MWC RNG, I’d like to present our full code from the GSDEngine (modified slightly for public consumption). All of this code is released into the public domain.
Here’s a C-style version (but still compiled with C++ compiler):
// This is based off of George Marsaglia's post to Sci.Stat.Math
// http://www.bobwheeler.com/statistics/Password/MarsagliaPost.txt
// This is the Multiply With Carry random number generator, modified
// to use a single 64-bit seed to create a 32-bit RNG, rather than
// gluing together two 16-bit RNGs (and thus two seeds)
// Default V value is the conglomeration of the default Z and W values from the Marsaglia MWC RNG
#define DEFAULT_V_VALUE 0x159A55E51F123BB5
struct RAND
{
unsigned long long v;
};
inline void RandInit(RAND& tRand, unsigned long long seed)
{
if(seed == 0)
seed = DEFAULT_V_VALUE;
tRand.v = seed;
}
// Gets a number in the range [0, UINT_MAX]
inline unsigned int RandGetNum(RAND& tRand)
{
tRand.v = 36969 * (tRand.v & 0xFFFFFFFF) + (tRand.v >> 32);
return tRand.v & 0xFFFFFFFF;
}
// Gets a random number in the range [0, Max) (that is, from 0 to Max-1)
inline unsigned int RandGetNum(RAND& tRand, unsigned int nMax)
{
return nMax ? RandGetNum(tRand) % nMax : RandGetNum(tRand);
}
// Gets a random number in the range [Min, Max]
// You will get weird results if Min is greater than Max
inline int RandGetNum(RAND& tRand, int nMin, int nMax)
{
return RandGetNum(tRand, (nMax - nMin + 1)) + nMin;
}
// Returns a floating point number in the range [0,1]
inline float RandGetFloat(RAND& tRand)
{
const float fInverse = 1.0f / (float)0xFFFFFFFF;
return RandGetNum(tRand) * fInverse;
}
Or, if you prefer C++:
// This is based off of George Marsaglia's post to Sci.Stat.Math
// http://www.bobwheeler.com/statistics/Password/MarsagliaPost.txt
// This is the Multiply With Carry random number generator, modified
// to use a single 64-bit seed to create a 32-bit RNG, rather than
// gluing together two 16-bit RNGs (and thus two seeds)
// Default V value is the conglomeration of the default Z and W values from the Marsaglia MWC RNG
#define DEFAULT_V_VALUE 0x159A55E51F123BB5
class RandomNumberGenerator
{
private:
unsigned long long v;
public:
RandomNumberGenerator(unsigned long long seed = 0)
{
if(seed == 0)
seed = DEFAULT_V_VALUE;
v = seed;
}
// Gets a number in the range [0, UINT_MAX]
unsigned int GetNum()
{
v = 36969 * (v & 0xFFFFFFFF) + (v >> 32);
return v & 0xFFFFFFFF;
}
// Gets a random number in the range [0, Max) (that is, from 0 to Max-1)
unsigned int GetNum(unsigned int nMax)
{
return nMax ? GetNum() % nMax : GetNum();
}
// Gets a random number in the range [Min, Max]
// You will get weird results if Min is greater than Max
int GetNum(int nMin, int nMax)
{
return GetNum((nMax - nMin + 1)) + nMin;
}
// Returns a floating point number in the range [0,1]
float GetFloat()
{
const float fInverse = 1.0f / (float)0xFFFFFFFF;
return GetNum() * fInverse;
}
// Gets a random number in the range [0, Max) (that is, from 0 to Max-1)
unsigned int operator ()(unsigned int nMax = 0)
{
return nMax ? GetNum(nMax) : GetNum();
}
};
Let’s write some code to use this RNG:
int main()
{
RAND tRand;
RandInit(tRand, 0);
unsigned int nRandom = RandGetNum(tRand);
printf("%u\n", nRandom);
return 0;
}
Or, with the C++ class:
int main()
{
RandomNumberGenerator tRand;
unsigned int nRandom = tRand.GetNum();
printf("%u\n", nRandom);
return 0;
}
Next time, we’ll really delve into compiler optimizations, I promise!
Leave a Reply