AxlRand — deterministic pseudo-random numbers
Mirrors GLib’s GRand: a seedable, reproducible PRNG
(xoshiro256** seeded through SplitMix64) independent of any firmware
entropy source. It is the deterministic complement to
AxlRng — cryptographic random bytes — reach for AxlRand when you want repeatable streams (test
fixtures, sampling, retry-backoff jitter, procedural graphics,
shuffling) and for axl_rng_bytes when you need cryptographic
material.
A given seed produces a byte-identical stream on x86-64 and AArch64.
The output is defined as a sequence of 64-bit words and every call is
pinned to it: axl_rand_uint32 returns the high 32 bits of the next
word, axl_rand_double maps the top 53 bits into [0, 1), and
axl_rand_bytes emits successive words little-endian. int_range
is unbiased (rejection-sampled) and double_range rounds its scaled
product before adding the offset so no fused multiply-add can perturb
the low bit.
A process-global stream (axl_random_*, mirroring g_random_*)
is lazily seeded from a non-reproducible source; call
axl_random_set_seed first for a deterministic global sequence.
AXL_AUTOPTR(AxlRand) r = axl_rand_new_seeded(0x1234);
uint32_t a = axl_rand_uint32(r);
int d = axl_rand_int_range(r, 1, 7); // dice roll, [1,7)
double t = axl_rand_double(r); // [0.0, 1.0)
API Reference
Deterministic pseudo-random number generator.
Mirrors GLib’s GRand. A seedable, reproducible PRNG that is independent of any firmware entropy source — the complement to axl-rng.h (axl_rng_bytes, which draws from the hardware EFI_RNG_PROTOCOL and has no deterministic mode). Reach for AxlRand when you want repeatable streams: test fixtures, sampling, retry-backoff jitter, procedural graphics (noise, particles), shuffling.
The generator is xoshiro256** seeded through SplitMix64. A given seed produces the identical stream on every architecture — the output is defined as a sequence of 64-bit words w0, w1, w2, …, and every generator call is pinned to that sequence:
axl_rand_uint64returns the next word and advances one step.axl_rand_uint32returns the high 32 bits of the next word (one step). It is a narrowing of the 64-bit stream, not a separate generator:uint32anduint64draw from the same sequence, one word per call.axl_rand_doublemaps the next word’s top 53 bits into [0,1).axl_rand_booleanreturns bit 63 of the next word.axl_rand_bytesemits successive words in little-endian byte order (low byte first), truncating the final word.
All of these are bit-identical across x86-64 and AArch64 (pure 64-bit integer math, plus the standard 53-bit double construction). axl_rand_double_range is likewise bit-identical: the implementation rounds the scaled product to double before adding the offset, so no fused multiply-add can perturb the low bit. The generator is fast and statistically strong but is NOT cryptographically secure — use axl_rng_bytes for nonces, keys, and tokens.
// Reproducible stream:
AXL_AUTOPTR(AxlRand) r = axl_rand_new_seeded(0x1234);
uint32_t a = axl_rand_uint32(r);
int d = axl_rand_int_range(r, 1, 7); // dice roll, [1,7)
double t = axl_rand_double(r); // [0.0, 1.0)
// One-off without managing a handle (process-global stream):
if (axl_random_boolean())
axl_printf("heads\n");
Functions
-
AxlRand *axl_rand_new(void)
Create a generator seeded from a non-reproducible source.
Seeds from
axl_rng_bytes()when the firmware entropy protocol is available, otherwise from a monotonic clock reading. Two calls therefore yield different streams. For a repeatable stream use axl_rand_new_seeded(). Never returns NULL (aborts on OOM, per the library’s allocation contract).- Returns:
a new generator; free with axl_rand_free().
-
AxlRand *axl_rand_new_seeded(uint64_t seed)
Create a generator with an explicit 64-bit seed.
Deterministic: the same
seedalways produces the same stream. Every seed value is valid (a zero seed is handled — the SplitMix64 expansion guarantees a non-degenerate xoshiro state).- Parameters:
seed – seed value
- Returns:
a new generator; free with axl_rand_free().
-
AxlRand *axl_rand_copy(const AxlRand *r)
Duplicate a generator, including its current position.
The copy continues the identical stream from the point of the copy, independently of the original. Useful for branching a reproducible sequence. NULL-safe: returns NULL when
ris NULL.- Parameters:
r – generator to copy
- Returns:
a new generator, or NULL if
ris NULL.
-
void axl_rand_set_seed(AxlRand *r, uint64_t seed)
Re-seed an existing generator in place.
Resets the stream as if the generator had just been created with axl_rand_new_seeded(
seed). No-op whenris NULL.- Parameters:
r – generator
seed – new seed value
-
void axl_rand_free(AxlRand *r)
Free a generator. NULL-safe.
- Parameters:
r – generator (may be NULL)
-
uint32_t axl_rand_uint32(AxlRand *r)
Next 32-bit value, uniform over the full range.
- Parameters:
r – generator
- Returns:
a value in [0, 2^32).
-
uint64_t axl_rand_uint64(AxlRand *r)
Next 64-bit value, uniform over the full range.
The generator’s native word — no narrowing. Prefer this over two axl_rand_uint32() calls when 64 bits are needed.
- Parameters:
r – generator
- Returns:
a value in [0, 2^64).
-
int32_t axl_rand_int_range(AxlRand *r, int32_t begin, int32_t end)
Uniform integer in the half-open range [
begin,end).Rejection-sampled, so the result is unbiased across the whole range (no modulo skew). The span is computed in 64-bit arithmetic, so every
begin<endpair is valid — including the full [INT32_MIN, INT32_MAX) range whose width exceeds INT32_MAX. A degenerate range (begin>=end) returnsbeginwithout drawing from the stream. Note there is no 64-bit range variant — for an unbiased 64-bit range, layer rejection sampling over axl_rand_uint64().- Parameters:
r – generator
begin – inclusive lower bound
end – exclusive upper bound
- Returns:
a value v with
begin<= v <end, orbeginif the range is empty.
-
double axl_rand_double(AxlRand *r)
Uniform double in the half-open range [0.0, 1.0).
53 bits of resolution (one per representable double in the range).
- Parameters:
r – generator
- Returns:
a value in [0.0, 1.0).
-
double axl_rand_double_range(AxlRand *r, double begin, double end)
Uniform double in the half-open range [
begin,end).A degenerate range (
begin>=end) returnsbegin.- Parameters:
r – generator
begin – inclusive lower bound
end – exclusive upper bound
- Returns:
a value v with
begin<= v <end, orbeginif the range is empty.
-
bool axl_rand_boolean(AxlRand *r)
Fair coin flip.
- Parameters:
r – generator
- Returns:
true or false, each with probability 0.5.
-
void axl_rand_bytes(AxlRand *r, void *out, size_t len)
Fill a buffer with
lenpseudo-random bytes.Bytes are drawn from successive 64-bit stream words emitted little-endian (see the file header). Deterministic for a given seed and byte-identical across architectures — unlike axl_rng_bytes(), which draws hardware entropy and returns a status. This call cannot fail (returns void).
len== 0 is a no-op. Do NOT use for cryptographic material.- Parameters:
r – generator
out – destination buffer
len – number of bytes to write
-
void axl_random_set_seed(uint64_t seed)
Seed the process-global generator for a reproducible stream.
May be called at any time; resets the global stream to seed
seedregardless of whether it had already been lazily seeded or used. Call this before the first axl_random_* use in a test to get a deterministic global sequence.- Parameters:
seed – seed value
-
uint32_t axl_random_uint32(void)
Next 32-bit value from the global generator.
See also
-
int32_t axl_random_int_range(int32_t begin, int32_t end)
Uniform integer in [
begin,end) from the global generator.See also
- Parameters:
begin – inclusive lower bound
end – exclusive upper bound
-
double axl_random_double(void)
Uniform double in [0.0, 1.0) from the global generator.
See also
-
double axl_random_double_range(double begin, double end)
Uniform double in [
begin,end) from the global generator.See also
- Parameters:
begin – inclusive lower bound
end – exclusive upper bound
-
bool axl_random_boolean(void)
Fair coin flip from the global generator.
See also