zrand.h is a high-performance, cross-platform Random Number Generator (RNG) library for C and C++. It implements the PCG-XSH-RR algorithm, which offers statistically superior randomness compared to the standard rand() (Linear Congruential Generator) while remaining extremely fast and memory-efficient.
It also includes a robust C++11 wrapper (z_rand namespace) that integrates with STL containers and provides a modern, fluent API.
- Statistically Robust: Uses the PCG algorithm (Permuted Congruential Generator). Passes rigorous statistical tests (TestU01/BigCrush) where standard
rand()fails. - Bias-Free Ranges: Implements rejection sampling for
zrand_range(), eliminating the "modulo bias" inherent inrand() % N. - Cross-Platform Entropy: Automatically seeds from the OS CSPRNG (
/dev/urandomon Linux/macOS,rand_son Windows). - Thread Safety: Uses thread-local state by default. No locks required for global generation.
- Deterministic Mode: Supports explicit seed/sequence initialization for replay systems or procedural generation.
- Gaussian Distribution: Built-in Box-Muller transform for normal distributions.
- Zero Dependencies: Standard C headers only. Can optionally use
zmath.hfor math functions to avoid linking-lm, or falls back to<math.h>. - Header Only: No linking required.
- Copy
zrand.hto your project's include folder. - In exactly one source file (
.cor.cpp), define the implementation macroZRAND_IMPLEMENTATIONbefore including the header.
#define ZRAND_IMPLEMENTATION
#include "zrand.h"If you want to avoid linking against the standard math library (-lm) or want a strictly freestanding environment, you can use zmath.h.
The provided Makefile handles this automatically for testing:
# For running the tests. Will get and remove 'zmath.h'.
make test
# Or if you just want to get 'zmath.h'.
makeFor C projects, you can use the global API for immediate results without managing state. The library handles seeding automatically.
#define ZRAND_IMPLEMENTATION
#define ZRAND_SHORT_NAMES
#include "zrand.h"
#include <stdio.h>
int main(void)
{
// Auto-seeds from OS entropy on first use. No srand() needed.
// Basic generation.
printf("d20 Roll: %d\n", rand_range(1, 20));
printf("Float (0..1): %.4f\n", rand_f32());
// Utilities.
if (rand_chance(0.05))
{
printf("Critical Hit! (5%% chance)\n");
}
// Gaussian (mormal) distribution (Mean=0, StdDev=1).
printf("Normal Value: %.4f\n", rand_gaussian(0.0, 1.0));
// UUID generation.
char uuid[37];
rand_uuid(uuid);
printf("New ID: %s\n", uuid);
return 0;
}Note: You can use #define ZRAND_SHORT_NAMES to access functions as rand_xyz instead of zrand_xyz.
The library detects C++ automatically. The wrapper lives in the z_rand namespace.
#include <iostream>
#include <vector>
#include "zrand.h"
int main()
{
// A native-like API.
std::cout << "d6 Roll: " << z_rand::range(1, 6) << "\n";
std::cout << "UUID: " << z_rand::uuid() << "\n"; // Returns std::string.
// STL integration.
std::vector<int> deck = {1, 2, 3, 4, 5};
z_rand::shuffle(deck); // Works directly on std::vector.
// Deterministic Instance.
z_rand::generator rng(12345); // Fixed seed.
std::cout << "Replay Value: " << rng.range(1, 100) << "\n";
return 0;
}These functions use a high-performance thread-local generator instance. It is automatically seeded from the OS on the first call in each thread.
| Function | Description |
|---|---|
uint32_t zrand_u32(void) |
Returns a random 32-bit unsigned integer. |
uint64_t zrand_u64(void) |
Returns a random 64-bit unsigned integer. |
float zrand_f32(void) |
Returns a float in the range [0.0, 1.0) (exclusive). |
double zrand_f64(void) |
Returns a double in the range [0.0, 1.0) (exclusive). |
bool zrand_bool(void) |
Returns true or false (50/50 probability). |
| Function | Description |
|---|---|
int32_t zrand_range(int32_t min, int32_t max) |
Returns int32_t in range [min, max] (inclusive). Bias-free. |
float zrand_range_f(float min, float max) |
Returns float in range [min, max). |
bool zrand_chance(double probability) |
Returns true if a random check passes the given probability (0.0 to 1.0). |
double zrand_gaussian(double mean, double stddev) |
Returns a double following a normal distribution. |
void zrand_bytes(void *buf, size_t len) |
Fills a buffer with random bytes. |
void zrand_str(char *buf, size_t len) |
Fills a buffer with a random alphanumeric string (A-Z, a-z, 0-9). |
void zrand_uuid(char *buf) |
Generates a valid UUID v4 string. buf must be at least 37 bytes. |
void zrand_shuffle(void *base, size_t nmemb, size_t size) |
Shuffles an array in-place using Fisher-Yates. |
void* zrand_choice(void *base, size_t nmemb, size_t size) |
Returns a void* pointer to a random element in the array. |
| Function | Description |
|---|---|
void zrand_init(void) |
Explicitly re-seeds the current thread's generator from OS entropy (/dev/urandom or rand_s). |
Use these functions when you need reproducible sequences (e.g., for game replays, procedural generation, or simulations) where the randomness must be identical given the same seed.
| Function | Description |
|---|---|
void zrand_rng_init(zrand_rng *rng, uint64_t seed, uint64_t seq) |
Initializes a specific zrand_rng struct. seq (sequence) allows different streams from the same seed. |
| Function | Description |
|---|---|
uint32_t zrand_rng_u32(zrand_rng *rng) |
Helper to generate a 32-bit int from a specific instance. |
uint64_t zrand_rng_u64(zrand_rng *rng) |
Helper to generate a 64-bit int from a specific instance. |
int32_t zrand_rng_range(zrand_rng *rng, int32_t min, int32_t max) |
Helper to generate a range from a specific instance. |
double zrand_rng_f64(zrand_rng *rng) |
Helper to generate a double from a specific instance. |
double zrand_rng_gaussian(zrand_rng *rng, double mean, double stddev) |
Helper to generate a gaussian double from a specific instance. |
The C++ wrapper is defined in the z_rand namespace.
Global Shortcuts
| Function | Description |
|---|---|
z_rand::init() |
Wraps zrand_init. |
z_rand::u32(), u64() |
Returns random integers. |
z_rand::f32(), f64() |
Returns random floating point numbers. |
z_rand::boolean() |
Returns boolean. |
z_rand::chance(prob) |
Returns true based on probability. |
z_rand::range(min, max) |
Returns value in range (overloaded for int and float). |
z_rand::gaussian(mean, std) |
Returns normally distributed value. |
z_rand::uuid() |
Returns a standard std::string containing a UUID v4. |
z_rand::string(len) |
Returns a standard std::string of random alphanumeric characters. |
z_rand::shuffle(vector) |
Shuffles a std::vector (or z_vec::vector) in-place. |
z_rand::choice(vector) |
Returns a random element (const ref) from the vector. |
The z_rand::generator class wraps the C struct state (zrand_rng). It provides methods matching the global API but operates on its own internal state.
// Seed 1234, Sequence 1.
z_rand::generator rng(1234);
int hp = rng.range(50, 100);
bool has_key = rng.chance(0.25);The default rand() in C is typically a Linear Congruential Generator (LCG). LCGs have statistical flaws:
- Short Periods: They repeat sequences relatively quickly.
- Poor Distribution: They can fail basic statistical tests (like the "Birthday Spacings" test).
- Low Bits: The lower bits of an LCG often exhibit visible patterns (odd/even/odd/even).
PCG (Permuted Congruential Generator) solves this by applying a permutation function (XSH-RR) to the output of an LCG. This scrambles the bits, resulting in:
- Excellent statistical quality (passes BigCrush).
- Performance that is often faster than
rand()due to modern CPU optimizations. - A huge period (2^64), making repeats virtually impossible in typical applications.