UniformFloat
Draw a uniform random value in using generator . is the primitive draw; all other randomization functions and distribution samplers build on top of it.
- Distribution —
- Range — (includes 0, excludes 1)
- Precision — 53-bit mantissa ( distinct values)
- Complexity — per draw
Properties
- Determinism same generator state produces same value
- Independence successive draws are uncorrelated
- Uniformity all representable values in equally likely
Example (conceptual)
- Call —
Rng("demo").UniformFloat()(conceptual name; see mapping below) - Repeat — 10 successive calls produce 10 independent values
Implementation names
| Language | Method |
|---|---|
| C# | UniformDouble() |
| Go | UniformFloat64() |
| Kotlin | uniformDouble() |
| Rust | uniform_f64() |
| Python | uniform_float() |
| R | uniform_float() |
| TypeScript | uniformFloat() |
is the fundamental operation of the random number generator. All other randomization functions — , , , and the distribution samplers — are built on top of uniform draws. See Naming for why the toolkit uses the name instead of the traditional .Next().
Algorithm
Generator Core
The core random number generator uses the xoshiro256++ algorithm (see Blackman & Vigna 2021), a member of the xoshiro/xoroshiro family developed by David Blackman and Sebastiano Vigna. This algorithm was selected for several reasons:
- Quality: passes all tests in the BigCrush test suite from TestU01
- Speed: extremely fast due to simple bitwise operations (shifts, rotations, XORs)
- Period: period of , sufficient for parallel simulations
- Adoption: used by .NET 6+, Julia, and Rusts rand crate
The generator maintains a 256-bit state () and produces 64-bit outputs. Each step updates the state through a combination of XOR, shift, and rotate operations.
Seed Initialization
Converting a single seed value into the full 256-bit state requires a seeding algorithm. The toolkit uses SplitMix64 (see Steele et al. 2014) for this purpose:
Four consecutive outputs from SplitMix64 initialize the xoshiro256++ state (). This approach provides high-quality initial states from simple integer seeds.
String Seeds
For named experiments (e.g., ), string seeds are converted to integers using FNV-1a hash (see Fowler et al. 1991):
This enables meaningful experiment identifiers while maintaining determinism.
UniformFloat Generation
To generate uniform values in , the upper 53 bits of a 64-bit output are used:
The 53-bit mantissa of IEEE 754 double precision ensures all representable values in are reachable.
UniformInt Mapping
maps a uniform 64-bit value into using modulo reduction. Ranges that do not divide introduce a slight bias (acceptable for simulation, not for cryptographic use).
Numerical Constants
Distribution sampling requires handling edge cases where floating-point operations would be undefined. Two constants are used across all language implementations:
Machine Epsilon (): The smallest such that in float64 arithmetic.
Used when to avoid or division by zero in inverse transform sampling.
Smallest Positive Subnormal (): The smallest positive value representable in IEEE 754 binary64.
Used when to avoid in transforms that take a logarithm.
All language implementations use the same literal values for these constants (not language-specific builtins like Number.EPSILON or f64::EPSILON) to ensure bit-identical outputs across languages.
Distribution Sampling
All distribution samplers consume one or more draws. The specific transforms are documented on the corresponding distribution pages.
Notes
Most programming languages expose the primary PRNG operation as .Next(), .random(), or rand() — a method name that describes the mechanism (advance the internal state and return a value) rather than the result (a uniformly distributed number on ).
This toolkit names the operations and — combining the distribution name with the return type. The reasons are both pedagogical and practical. In actual code the method names are language-specific and type-suffixed; see the UniformFloat and UniformInt pages for the mapping.
The name communicates the contract. Calling immediately tells the reader what distribution the returned value follows and what type it produces. Calling r.Next() says only that something comes next; the distribution, range, and precision are left to documentation. In a library that manipulates multiple distributions (, , , , ), naming the uniform draw explicitly makes it a peer of the other distributions rather than a special primitive hidden behind a generic verb.
The name prevents a category error. When random() returns a value in , users sometimes treat it as a random number without recognizing that it samples from a specific distribution. Making the distribution explicit in the name reinforces that is one choice among many and that other distributions require different transformations.
The name preserves the URL namespace. Using for the function frees /uniform for the distribution page, avoiding ambiguity between the function (which draws a single value) and the distribution family (which defines the parametric model).
Composition becomes self-documenting. When a distribution is built from uniform draws, the code reads naturally as take draws and apply a transformation, keeping the distribution explicit. Replacing with .Next() in these descriptions obscures the mathematical structure.
Precedent. Scientific computing libraries (NumPys random.uniform, Rs runif, Julias rand(Uniform())) already use uniform when the distribution matters. The toolkit follows this convention consistently: every random draw is named after its distribution, starting with the simplest one.