dotnet / dotnet/dotnet-api-docs

Better examples to generate `Int64` values using the `Random` class

Open
#6,997 5 comments 0 reactions 0 assignees View on GitHub
area-System.Runtime Pri3 untriaged
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

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.