This is how I do it.
private static readonly System.Security.Cryptography.RNGCryptoServiceProvider _secureRng;
public static double NextSecureDouble()
{
var bytes = new byte[8];
_secureRng.GetBytes(bytes);
var v = BitConverter.ToUInt64(bytes, 0);
v &= ((1UL << 53) - 1);
var r = (double)v / (double)(1UL << 53);
return r;
}
Coincidentally 9007199254740991 / 9007199254740992 is ~= 0.99999999999999988897769753748436, that will return the method Random.NextDoubleas the maximum value (see https://msdn.microsoft.com/en-us/library/system.random.nextdouble(v=vs.110).aspx ).
(max - min)/sqrt (12).
1000 2%.
10000 1%.
.
[Test]
public void Randomness_SecureDoubleTest()
{
RunTrials(1000, 0.02);
RunTrials(10000, 0.01);
}
private static void RunTrials(int sampleSize, double errorMargin)
{
var q = new Queue<double>();
while (q.Count < sampleSize)
{
q.Enqueue(Randomness.NextSecureDouble());
}
for (int k = 0; k < 1000; k++)
{
q.Dequeue();
q.Enqueue(Randomness.NextSecureDouble());
var avg = q.Average();
var actual = Math.Sqrt(q.Sum(x => (x - avg) * (x - avg)) / (q.Count - 1));
var expected = (q.Max() - q.Min()) / Math.Sqrt(12);
Assert.AreEqual(expected, actual, errorMargin);
}
}