I would like to use RNGCryptoServiceProvider as my source of random numbers. As it only can output them as an array of byte values how can I convert them to 0 to 1 double value while preserving uniformity of results?
4 Answers
byte[] result = new byte[8];
rng.GetBytes(result);
return (double)BitConverter.ToUInt64(result,0) / ulong.MaxValue;

- 414,610
- 91
- 852
- 789
-
My brain can't handle simple math today. That's exactly what I was trying to do. +1 to you. – Jon B Dec 31 '08 at 18:05
-
Yeah this is much better... The situation with 1.0/value is horrible... You only need a double value > 2 before you are already halfway from 1 to 0. There are many more doubles from 2..double.Max than there is from 0..2. – Michael Bray Dec 31 '08 at 18:09
-
It seems that this solution could (if you really are (un)lucky) return 1. But Random.NextDouble() returns a value between 0 inclusive and 1 exclusive. – krimog Aug 19 '14 at 12:02
-
I don't get why it's `ulong.MaxValue` instead of `double.MaxValue`, also, doesn't this return numbers in the range [-1, +1]? And finally, isn't there a `3` in `2^64` chance to get a `NaN` or `Infinity` this way? – Fabio Iotti Jan 09 '15 at 20:18
-
2@bruce965 **First question:** it's because `ulong.MaxValue` is the biggest number you can get in the numerator. **Second question:** No because the numerator is always positive (it's unsigned). **Third question:** No idea what you are talking about... what does `3` have to do with anything? `2^64` is much smaller than `double.MaxValue` so no `Infinity` and the only way it could become `NaN` is if the denominator is zero, which it isn't. **Edit:** Perhaps you thought the cast to `double` was blitting? It isn't. – AnorZaken Sep 26 '15 at 19:08
-
I don't know what I was thinking when I asked that, you are totally right at all of the three answers, thanks. – Fabio Iotti Sep 27 '15 at 09:02
-
@AnorZaken A double can only carry 53-bits of integer precision. I'm not sure of the implications of this though. However, if you simply do `x / ulong.MaxValue` you have a situation where you may return `1.0` while the typically `NextDouble` API should return a value less than `1.0` so that you can safely use it to generate an index into an array and don't have to worry about out of bounds. For example `arr[(int)(r * arr.Length)]` where `r` is in the range `[0, 1)` that is `0.0 <= r && r < 1.0`. – John Leidegren Oct 25 '15 at 09:04
-
@JohnLeidegren It means several things! (54bits though.) Firstly: 10 rng-bits are wasted so this would be faster if one used the c# equivalent of a struct union of a long and 7 bytes (where the 2 highest bits of the highest byte are always zero) instead of BitConverter... but there is also the question of code readability. Secondly your concern about 1.0: `(double)(ulong.MaxValue - 1) / ulong.MaxValue` would still result in 1.0 due to truncation in the cast! This is the stronger reason for using only 54bits of random! Using any more than that makes it tricky to properly prevent 1.0 (cont...) – AnorZaken Oct 28 '15 at 03:21
-
(cont.) The proper way to do this is to start with 53bits of random. Next shift left one position. If all 53bits were ones this results in 2^54 - 2, in which case we are done because (2^54 - 2) / (2^54 - 1) will yield the largest possible double value below 1.0. Otherwise get one more bit of random from your random source and add this as the least significant bit using either the | (bitwise-or) or + (addition) operator. So now we have `long bits; //54 bits of random [0 - 0x003FffffFFFFfffe]` and finally `double value = bits * (1.0 / 0x003FffffFFFFffff);` (notice one ends `e` and the other `f`) – AnorZaken Oct 28 '15 at 03:36
-
Correction: it _is_ 53-bits as you said, but (2^54 - 2) * (1.0 / (2^54 - 1)) will yield the largest possible double value below 1.0 just as stated. – AnorZaken Oct 28 '15 at 04:03
This is how I would do this.
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);
// We only use the 53-bits of integer precision available in a IEEE 754 64-bit double.
// The result is a fraction,
// r = (0, 9007199254740991) / 9007199254740992 where 0 <= r && r < 1.
v &= ((1UL << 53) - 1);
var r = (double)v / (double)(1UL << 53);
return r;
}
Coincidentally
9007199254740991 / 9007199254740992 is ~= 0.99999999999999988897769753748436
which is what theRandom.NextDouble
method will return as it's maximum value (see https://msdn.microsoft.com/en-us/library/system.random.nextdouble(v=vs.110).aspx).
In general, the standard deviation of a continuous uniform distribution is (max - min) / sqrt(12).
With a sample size of 1000 I'm reliably getting within a 2% error margin.
With a sample size of 10000 I'm reliably getting within a 1% error margin.
Here's how I verified these results.
[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++)
{
// rotate
q.Dequeue();
q.Enqueue(Randomness.NextSecureDouble());
var avg = q.Average();
// Dividing by n−1 gives a better estimate of the population standard
// deviation for the larger parent population than dividing by n,
// which gives a result which is correct for the sample only.
var actual = Math.Sqrt(q.Sum(x => (x - avg) * (x - avg)) / (q.Count - 1));
// see http://stats.stackexchange.com/a/1014/4576
var expected = (q.Max() - q.Min()) / Math.Sqrt(12);
Assert.AreEqual(expected, actual, errorMargin);
}
}

- 59,920
- 20
- 131
- 152
-
I think that this should be the accepted answer, as it has the same behavior as Random in that it never returns 1.0. Though correct me if I'm wrong... you can simplify part of the code a bit: `v &= ((1UL << 53) - 1)` is the same as `v >> 11` isn't it? – Yellowfive Apr 05 '16 at 07:44
-
@Yellowfive Sure, however `v &= ((1UL << 53) - 1)` is a common pattern that is used to mask bits. I've not seen bit shift being used to mask bits. Moreover the format has 53 in it's construction which will tell you that there's exactly 53 significant bits of information left after the mask operation is done. This is why I prefer this method of masking. – John Leidegren Apr 05 '16 at 08:24
You can use the BitConverter.ToDouble(...) method. It takes in a byte array and will return a Double. Thre are corresponding methods for most of the other primitive types, as well as a method to go from the primitives to a byte array.

- 14,998
- 7
- 42
- 68
-
There are special cases for a double (like NaN) that you'd want to avoid. – Jon B Dec 31 '08 at 17:50
-
-0 Jon is right. The worse thing is the number is not between 0 and 1. – Mehrdad Afshari Dec 31 '08 at 17:55
-
Try this: BitConverter.GetBytes(double.NaN). Returns and 8 byte array ending in 248, 255. – Jon B Dec 31 '08 at 17:56
Use BitConverter to convert a sequence of random bytes to a Double:
byte[] random_bytes = new byte[8]; // BitConverter will expect an 8-byte array
new RNGCryptoServiceProvider().GetBytes(random_bytes);
double my_random_double = BitConverter.ToDouble(random_bytes, 0);

- 3,457
- 4
- 31
- 41
-
-
Thank you but results would be very ununiform, with their denisty increasing towards 0 and no chance to get anything between 0.5 and 1 – Kamil Zadora Dec 31 '08 at 18:43