The problem is that you are doing the modulo operation. This is not a problem if RAND_MAX will evenly share your module, but usually it is not. As a very far-fetched example, suppose RAND_MAX is 11 and your module is 3. You will get the following possible random numbers and the following leftovers:
0 1 2 3 4 5 6 7 8 9 10 0 1 2 0 1 2 0 1 2 0 1
As you can see, 0 and 1 are slightly more likely than 2.
One of the solutions to this problem is to select a reject: by prohibiting the numbers 9 and 10 above, you can cause the resulting distribution to be uniform. The hard part is figuring out how to do it effectively. A very good example (the one that took me two days to understand why it works) can be found in Java java.util.Random.nextInt(int) .
The reason the Java algorithm is a bit complicated is because they avoid slow operations such as multiplication and division for validation. If you care, you can also do it naively:
int n = (int)(max - min + 1); int remainder = RAND_MAX % n; int x, output; do { x = rand(); output = x % n; } while (x >= RAND_MAX - remainder); return min + output;
EDIT: Fixed fencepost error in the above code, now it works as it should. I also created a small trial program (C #; taking a single PRNG for numbers from 0 to 15 and building a PRNG for numbers from 0 to 6 from it in various ways):
using System; class Rand { static Random r = new Random(); static int Rand16() { return r.Next(16); } static int Rand7Naive() { return Rand16() % 7; } static int Rand7Float() { return (int)(Rand16() / 16.0 * 7); } // corrected static int Rand7RejectionNaive() { int n = 7, remainder = 16 % n, x, output; do { x = Rand16(); output = x % n; } while (x >= 16 - remainder); return output; } // adapted to fit the constraints of this example static int Rand7RejectionJava() { int n = 7, x, output; do { x = Rand16(); output = x % n; } while (x - output + 6 > 15); return output; } static void Test(Func<int> rand, string name) { var buckets = new int[7]; for (int i = 0; i < 10000000; i++) buckets[rand()]++; Console.WriteLine(name); for (int i = 0; i < 7; i++) Console.WriteLine("{0}\t{1}", i, buckets[i]); } static void Main() { Test(Rand7Naive, "Rand7Naive"); Test(Rand7Float, "Rand7Float"); Test(Rand7RejectionNaive, "Rand7RejectionNaive"); } }
The result is as follows (paste into Excel and add conditional coloring of the cells, so that the differences are more obvious):

Now that I have corrected my mistake in the rejected sample, it works as it should (before it moves to 0). As you can see, the float method is not perfect at all, it just distributes the offset numbers differently.