I've corrected the bug in the first version and fixed up the error messages.
As for using floating arithmetic, since i'm not using more than 10 digits of the generated double, which, to my understanding, has at least 14 significant digits I can't see how the rounding will effect the outcome.
As for the randomness of the results, I can't seem to find any bias in the results of this algorithm. Perhaps someone has a better test and hard numbers, instead of blind theory.
public static class CryptoRandom
{
const double MAX_RANGE = (double)UInt64.MaxValue + 1;
/// <summary>
/// Internal algorithm to generate the range based integers.
/// </summary>
static int Next(UInt64 min, UInt64 max)
{
if (max < min)
{
throw new ArgumentException($"max is less than min. The values are, min = {min}, max = {max}");
}
if(min < 0)
{
throw new ArgumentException($"min is negative. The value is min = {min}");
}
if (min == max)
{
throw new ArgumentException($"min equals max. The values are, min = {min}, max = {max}");
}
using (RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider())
{
byte[] randomNumber = new byte[8];
rng.GetBytes(randomNumber);
double baseNum = BitConverter.ToUInt64(randomNumber, 0) / MAX_RANGE;
UInt64 range = max - min;
return (int)((baseNum * range) + min);
}
}
/// <summary>
/// Get a cryptographic random 32-bit integer in the range from
/// min(inclusive) to max(exclusive)
/// </summary>
public static int Next(int min, int max)
{
return (int)Next((UInt64)min, (UInt64)max);
}
/// <summary>
/// Get a cryptographic random 32-bit integer in the range from
/// 0 to max(exclusive)
/// </summary>
public static int Next(int max)
{
return (int)Next(0, (UInt64)max);
}
/// <summary>
/// Get a cryptographic random 32-bit integer
/// </summary>
public static int Next()
{
return (int)Next(0, (UInt64)Int32.MaxValue+1);
}
}