Technology · 8 min read
Modulo Bias: The Subtle Math Behind Fair Random Selection
How a single line of code can quietly skew a random draw, why the bias is invisible at small scale, and how to write a selection algorithm that is provably uniform.
Most programmers, when asked to pick a random item from a list of N options, write something like this: a random 32-bit number modulo N. It looks fine. It compiles. It even produces results that pass casual inspection. But this innocent line of code is the source of one of the most persistent, and most overlooked, sources of unfairness in software: modulo bias.
Modulo bias is the reason that a poorly written shuffle in a major video game once gave one outcome a 14% higher chance than another. It is the reason cryptographic protocols spell out, in explicit detail, exactly how to draw a uniform integer in a range. And it is the reason that any spinner wheel, raffle tool, or contest engine that claims to be fair needs to know about it.
What the Bias Actually Is
Imagine a random number generator that produces uniformly distributed integers from 0 to 9, inclusive. You want a fair coin flip, so you take the result modulo 2. Even values (0, 2, 4, 6, 8) map to heads. Odd values (1, 3, 5, 7, 9) map to tails. The math works out perfectly: 5 outcomes on each side, 50% each.
Now imagine you want to pick from 3 options instead. You take the result modulo 3. Values 0, 3, 6, 9 map to option A — four chances. Values 1, 4, 7 map to option B — three chances. Values 2, 5, 8 map to option C — three chances. Option A is 33% more likely than B or C. This is modulo bias in miniature.
The problem appears whenever the range of the random number generator is not evenly divisible by the number of buckets you are mapping it into. In a 10-value range divided by 3, the remainder is 1 — and that single extra value gives one bucket a disproportionate share.
Why It Hides in Production Code
Most programmers never notice modulo bias because they work with 32-bit or 64-bit random generators. A 32-bit unsigned integer can hold 4,294,967,296 distinct values. If you mod that by, say, 100, the bias is microscopic: 96 values give each of the first 96 buckets an equal share of 42,949,673 outcomes, and the last four buckets get one extra outcome each — a 0.0000023% advantage. Statistically invisible.
But the bias scales with the divisor. When the divisor is close to the range itself, bias can become severe. And in cryptographic applications, where adversaries actively look for tiny biases to exploit, even microscopic deviations matter. This is why standards like NIST SP 800-90A explicitly require rejection sampling for uniform integer generation.
The Fix: Rejection Sampling
The technique that eliminates modulo bias entirely is called rejection sampling. The idea is simple: pre-compute the largest multiple of N that fits inside the generator's range. Any random value that falls into the leftover region above this multiple is thrown away, and you draw again. Only values that fall inside the clean range are accepted.
In pseudocode: `limit = floor(MAX / N) * N; do { x = random(); } while (x >= limit); return x mod N;`. The discarded values are rare in practice — with a 32-bit generator and a small N, you almost never reject — but they are exactly the values that would have caused the bias. By refusing to use them, you guarantee that every bucket gets exactly the same number of underlying random values.
Real-World Cases Where Bias Mattered
In 2013, security researchers analyzed the random number generation in popular online poker software. Several products were found to have modulo bias severe enough to make certain hands measurably more or less likely than they should be in a fair shuffle. Players who reverse-engineered the bias were able to gain a small but real edge.
A 2017 audit of a major mobile game found that loot box odds, while advertised as uniform across rarity tiers, suffered from modulo bias in the random selection code. One legendary item was 8% more likely to drop than its peers — small enough to not be obvious, large enough to be measurable across millions of pulls.
How a Conscientious Spinner Wheel Handles It
When Roulety needs to choose which segment the wheel lands on, the algorithm uses `crypto.getRandomValues` to draw a 32-bit unsigned integer, but it does not simply modulo that by the number of segments. Instead, it uses rejection sampling: it computes the largest multiple of the segment count that fits within 2^32, draws random values until one lands inside that range, and then takes the modulus. The result is a guaranteed-uniform distribution across all segments, regardless of how many entries the user has added.
The Takeaway
Fairness in random selection is not the default. It is something you have to engineer for, especially when the cost of unfairness is invisible at small scale but real at scale. Whether you are writing a giveaway tool, a research sampling algorithm, or a security protocol, the question to ask is not just "am I using a good random number generator?" but also "am I using it correctly?" Modulo bias is the answer to the second question that most code gets wrong.
Recommended Reading
If you found this article useful, these books go deeper into the same topics. Each title is hand-picked for the material covered above.
- The Art of Computer Programming, Vol. 2: Seminumerical Algorithms by Donald E. Knuth — The canonical reference for random number generation, with detailed treatment of modulo bias and rejection sampling. View on Amazon
- Cryptography Engineering: Design Principles and Practical Applications by Niels Ferguson, Bruce Schneier, Tadayoshi Kohno — Explains why uniform sampling matters in security-critical code and how to verify it. View on Amazon
As an Amazon Associate, Roulety earns from qualifying purchases. This does not change the price you pay and helps support the writing on this site.
© 2026 Roulety. Free online spinner wheel for decisions, games, and fun.