dotnet / dotnet/dotnet-api-docs
Better examples to generate `Int64` values using the `Random` class
- Dominant language
- C#
- Stars
- 949
- Forks
- 1.7k
- Avg merge
- 3d 27m
- Merged PRs (30d)
- 49
Description
There are problems with the examples referred to in [the documentation of the `System.Random` class when it comes to generating random `Int64` values](https://docs.microsoft.com/en-us/dotnet/api/system.random?view=net-5.0#generate-random-64-bit-integers).
First of all, using a `double` with a mantissa of 53 bits to create a random value of 64 bits will of course only generate 1 out of 2^11 numbers in the range on average. So the first example of using `NextDouble` is entirely useless, regardless of the "proof" that it is well distributed.
Secondly, the description of how to generate it using `Random#Next()` is invalid as well. Unfortunately, the `Next` method creates a value in the range `[0..2^31 - 1)`, so it is excluding `long.maxValue`. Even if the missing bit (`2^63 -1 > 2^31 * 2^31`) is accounted for, the algorithm is still incorrect.
I'd propose a new code build around the existing test, but with a better generator of random values between `2^63` and `2^63 - 1`. It's much shorter, easier to understand, and - most importantly - correct.
```
private const long ONE_TENTH = 922337203685477581;
public static void NextTest()
{
Random rnd = new Random();
int[] count = new int[10];
// Generate 20 million long integers.
for (int ctr = 1; ctr <= 20000000; ctr++)
{
long number = NextLong(rnd, true);
// Categorize random numbers.
count[(int) (number / ONE_TENTH)]++;
}
// Display breakdown by range.
Console.WriteLine("{0,28} {1,32} {2,7}\n", "Range", "Count", "Pct.");
for (int ctr = 0; ctr <= 9; ctr++)
{
Console.WriteLine("{0,25:N0}-{1,25:N0} {2,8:N0} {3,7:P2}",
ctr * ONE_TENTH,
ctr < 9 ? ctr * ONE_TENTH + ONE_TENTH - 1 : Int64.MaxValue,
count[ctr],
count[ctr]/20000000.0);
}
}
public static long NextLong(Random random, bool includeLongMaxValue = false)
{
byte[] longBuf = new byte[sizeof(long)];
long longValue;
// Loop runs once if includeLongMaxValue is true.
// Loop will run more than once with a chance of 1 / 2^31 otherwise.
do
{
random.NextBytes(longBuf);
// Make sure that the retrieved value is zero or positive.
longValue = BitConverter.ToInt64(longBuf) & long.MaxValue;
}
while (!includeLongMaxValue && longValue == long.MaxValue);
return longValue;
}
```
Contributor guide
Assessment
This issue has not been assessed yet.