Like all random-number generators, std::rand()
produces numbers in a limited range. All the values are greater than or equal to 0, and all the values are less than or equal to RAND_MAX
. The value of RAND_MAX
depends on the implementation that you're using. You can look at it to see the upper limit:
std::cout << RAND_MAX << '\n';
There are techniques for creating larger or smaller ranges from the results of a random-number generator. With the newer <random>
classes you'd use std::uniform_int
to handle the details. With std::rand()
you have to roll your own code.
To reduce the range, most people's first instinct is to use the remainder, as the code in the question does. That's okay, sort of, but it can introduce distortions. As an extreme example, suppose RAND_MAX
is 32767, and you want to generate values in the range [0 .. 32766]
. So you use rand() % 32767
, which gives you values in that range. But there's a problem: whenever rand()
produces a 0 you get a 0, when it produces a 1 you get a 1, etc., up to 32766. But when rand()
produces 32767 you get 0. That is, you'll get 0 when rand()
produces 0 and when it produces 32767, but you'll get other values for only one particular result from rand()
. If rand()
is perfectly uniform (no RNG is), you'll get 0 twice as often as you get any other value. When the range you want to generate is much smaller than the total range that the RNG produces this distortion isn't as large, and might be acceptable.
To do it right you have to discard values. The simplest way to discard values is just to ignore values larger than what you want:
int res = rand();
while (res > my_max_value)
res = rand();
But if my_max_value
is small, this will throw away a lot of values, wasting a lot of time. So a better approach is to combine discarding with taking the remainder:
int max = ((RAND_MAX + 1) / my_max_value) * my_max_value;
int res = rand();
while (res >= max)
res = rand();
res %= my_max_value;
I haven't done that calculation exactly right; for example, if RAND_MAX
is equal to INT_MAX
, adding 1 will overflow. Fixing that is left as an exercise for the reader.
max
will be the largest multiple of my_max_value
that's less than or equal to RAND_MAX
. The code discards values that are greater than or equal to that, and when it finds an appropriate value it takes the remainder.
To create a range that's larger than the range of the RNG you "tile" multiple values. For example, if RAND_MAX
is 32767 and you need values in the range [0 .. 1073741823]
(that upper limit is (32767 << 15) + 32767) you'd call rand()
twice, shift one of the results left by 15, and add the other result. If you don't have such a convenient <g> value you have to create a range that's larger than your target range by tiling, then reduce the resulting range as I discussed earlier.
Note that all this stuff has to be done regardless of the RNG that you're using. It's much more convenient in C++11, when all those calculations are encapsulated in std::uniform_int
, and that's a good reason for using the C++11 RNGs and distributions.