Uniform Distribution from Integer in C#

When dealing with random numbers, one often needs to get a random floating point number between 0 and 1 (not inclusive). Unfortunately, most random generators only deal with integers and/or bytes. Since bytes can be easily converted to integers, question becomes: "How can I convert integer to double number in [0..1) range?"

Well, assuming you start with 64-bin unsigned integer, you can use something like this:

Code
ulong value = 1234567890; //some random value
byte[] buffer = BitConverter.GetBytes((ulong)0x3FF << 52 | value >> 12);
return BitConverter.ToDouble(buffer, 0) - 1.0;

To understand this code, you need to know how doubles are encoded. The first bit is sign and we can ignore it for this usage. 11 bits that follow belong to exponent and setting them all to 1 means we start at 2⁰ (aka 1). The remaining bits express fraction portion of the floating-point number.

With that in mind you you can see that 8-byte (64-bit) buffer is filled with double format combined of (almost) all 1's in exponent and the fraction portion containing random number. If we take that raw buffer and convert it into a double, we'll get a number in [1..2) range. Simply substracting 1.0 will place it in our desired [0..1) range. It's as good as distribution of a double can be (i.e. the maximum number of bits - 56 - are used).

This is as good as uniform distribution can get using 64-bit double.


PS: If we apply the same principle to the float, equivalent code will be something like this (assuming a 32-bit random uint as input):

Code (single)
uint value = 1234567890; //some random value
byte[] buffer = BitConverter.GetBytes((uint)0x7F << 23 | value >> 9);
return BitConverter.ToSingle(buffer, 0) - 1.0;

PPS: C#'s Random class uses code that's probably a bit easier to understand:

Code (C# random)
return value * (1.0 / Int32.MaxValue);

Unfortunately, this will use only 31 bits for distribution (instead of 52 available in double). This will cause statistical anomalies if used later to scale into a large integer range.

Leave a Reply

Your email address will not be published. Required fields are marked *