I like to use a single global PRNG in my c programs (similar to Javascript's Math.random
) because it simplifies things and the unpredictable nature of a global is quite fitting for a PRNG. I recently started using multithreading more so I adapted my global PRNG by making it atomic.
Besides the obvious, it is meant to be atomic in such a way that two threads requesting a pseudo-random number simultaneously won't get the same number in the sequence of pseudo-random numbers. Using a CAS-loop seems to be the only way of accomplishing this, if there is a better way do let me know.
Primarily I'm looking for confirmation that it actually is atomic. Note that struct randomic
isn't the same as randomic
, the latter is a typedef of _Atomic struct randomic
. Admittedly not the best naming ever.
The real magic happens in randomicNext
but the whole thing is pretty small so I've included all of it. It can also be found on github.
//include only once
#ifndef RANDOMIC_H
#define RANDOMIC_H
//process configuration
#ifdef RANDOMIC_STATIC
#define RANDOMIC_IMPLEMENTATION
#define RADEF static
#else //RANDOMIC_EXTERN
#define RADEF extern
#endif
//includes
#include <stdatomic.h>
#include <stdint.h>
//types
typedef _Atomic struct randomic {
uint32_t a, b, c, d;
} randomic;
//function declarations
RADEF void randomicSeed(randomic*, uint32_t);
RADEF float randomicFloat(randomic*, float, float);
RADEF double randomicDouble(randomic*, double, double);
RADEF uint32_t randomicNext(randomic*);
//implementation section
#ifdef RANDOMIC_IMPLEMENTATION
//function declarations
static struct randomic randomicInternal(struct randomic);
//public functions
RADEF void randomicSeed (randomic* rdic, uint32_t seed) {
struct randomic ctx;
ctx.a = 0xf1ea5eed;
ctx.b = ctx.c = ctx.d = seed;
for (int i = 0; i < 20; i++)
ctx = randomicInternal(ctx);
atomic_store(rdic, ctx);
}
RADEF float randomicFloat (randomic* rdic, float a, float b) {
return a + (b-a)*((float)randomicNext(rdic)/(float)UINT32_MAX);
}
RADEF double randomicDouble (randomic* rdic, double a, double b) {
return a + (b-a)*((double)randomicNext(rdic)/(double)UINT32_MAX);
}
RADEF uint32_t randomicNext (randomic* rdic) {
struct randomic ctx = atomic_load(rdic), ntx;
while (!atomic_compare_exchange_weak(rdic, &ctx, (ntx = randomicInternal(ctx))));
return ntx.d;
}
//internal functions
static struct randomic randomicInternal (struct randomic ctx) {
uint32_t e = ctx.a - ((ctx.b << 27)|(ctx.b >> 5));
ctx.a = ctx.b ^ ((ctx.c << 17)|(ctx.c >> 15));
ctx.b = ctx.c + ctx.d;
ctx.c = ctx.d + e;
ctx.d = e + ctx.a;
return ctx;
}
#endif //RANDOMIC_IMPLEMENTATION
#endif //RANDOMIC_H