Skip to content
/ whisky Public

A collection of stateless random number generators in C

License

Notifications You must be signed in to change notification settings

velipso/whisky

Repository files navigation

whisky

Whisky is a collection of stateless random number generators. These can be thought of as tiny hash functions, or pure noise functions. Stateless RNGs are useful for a variety of reasons.

All you need is whisky.h! It includes the hash functions, a short SHA-256 implementation, and some utility functions for creating floating point numbers.

Everything else in this repo is supplemental for those curious about how these functions were generated and tested.

These functions were found by searching through hundreds of thousands of candidates. They have been tested against the dieharder 3.31.1 statistical tests (screenshot), and pass with flying colors.

API

The functions are named in the format of:

static uint32_t whiskyX(...);
static uint32_t whiskyXalt(...);
static uint32_t whiskyXalt2(...);
static float whiskyXf(...);
static double whiskyXd(...);
  • X - the number of inputs (dimensions), from 1 to 5
  • alt / alt2 - alternate hash functions of equal quality

For example, whisky2 is a hash function with two inputs:

static uint32_t whisky2(uint32_t i0, uint32_t i1){
  // ...mix up the inputs i0 and i1...
  return mixedValue;
}

The whisky2alt hash function also takes two inputs, and is equal quality compared to whisky2 -- it just mixes things differently. The same is true for whisky2alt2.

Having an alternate is useful if you need more random bits from the same input. You can get 32-bits from whisky2, another 32-bits from whisky2alt, and another 32-bits from whisky2alt2 using the same input.

All the functions are in the single header file whisky.h and declared static. This allows the compiler to inline functions where possible.

If you need higher dimensions, you can chain hashes. For example, if you need 9-dimensions, you could do something like whisky2(whisky4(...), whisky5(...)).

SHA-256

The plain whiskyX hashes are not cryptographically secure, so just for fun, I've also provided a single chunk implementation of SHA-256 -- which is complete overkill for games, but it's small (about 50 lines) and easy to include.

void whisky_sha256(const uint32_t input[8], uint32_t output[8]);

This is not a full implementation -- it cannot take an arbitrary length input. It is specifically limited to perform a hash on 32 bytes of input, and outputs the 32 byte hash. You can hash an array in-place by setting input and output to the same array.

It's obviously much slower than the whiskyX functions, but it's still quite fast! And of course the quality is ridiculously good.

Floating Point

I've also provided two utility functions to create floating point values ranging from 0 (inclusive) to 1 (exclusive).

float whiskyf(uint32_t a);
double whiskyd(uint32_t a, uint32_t b);

Simply pass in 32 or 64 random bits (via a/b), and get out a floating point number.

For convenience, I've wired up the hashing functions to the floating point functions. For example, you can call:

float  randomFloat  = whisky2f(123, 456); // 0.80855942
double randomDouble = whisky2d(123, 456); // 0.8085594523896744

These will return the same value for the same input, but the double has more precision.

More Info