Skip to tool
ecech.
📊 Math & Statistics

A Random Byte Mod 6 Rolls Low 2.38% Too Often

256 does not divide by 6, so four faces get an extra byte each. Rejection sampling throws away 1.56% of draws and removes the bias entirely.

Range size

Bits used

Draws rejected

to stay uniform

Bias if we used %

What modulo would have done

Advertisement

How the calculation works

256 byte values shared between 6 outcomes roll 1 43 bytes roll 2 43 bytes roll 3 43 bytes roll 4 43 bytes roll 5 42 bytes roll 6 42 bytes 4 × 43 + 2 × 42 = 256 Low rolls are 2.38% more likely. Rejecting the last 4 byte values costs 1.56% of draws and makes it exact.

How to Use This Tool

Set a range and press generate. Numbers come from crypto.getRandomValues with rejection sampling, so every value in the range is equally likely.

Why % n is not uniform

The obvious way to turn a random byte into a number from 1 to 6 is to take the remainder. A byte holds 256 values, and 256 divided by 6 is 42 remainder 4 — so four of the six outcomes get one extra byte each.

Counted exactly: outcomes 0 to 3 each collect 43 byte values, outcomes 4 and 5 collect 42. That makes the low rolls 2.38% more likely than the high ones. At a range of 10 the excess is 4.00%.

Nobody notices this over a hundred rolls. It becomes real when the draw decides something worth money, or when it runs millions of times, or when someone is looking for an edge.

Rejection sampling, which is simpler than it sounds

Take the largest multiple of your range that fits — for a range of 6 in a byte, that is 252. Throw away any byte of 252 or more and draw again. What is left divides evenly, so the remainder is exactly uniform.

The cost is the discarded draws: 4 out of 256, which is 1.56%. In the worst case — a range just over half the space — you discard just under half your draws and still finish almost immediately, because each retry has an independent chance of succeeding.

This tool widens to 16 or 32 bits for larger ranges, which keeps the rejection rate small no matter how big the range is.

Two different questions uniform? Math.random() — yes, near enough unpredictable? Math.random() — no, the state can be recovered crypto.getRandomValues() — yes, both If a draw decides anything anyone would want to rig — a prize, a token, a password — uniform is not enough on its own.
Fairness and unpredictability are separate properties. Only one generator has both.

Uniform is not the same as unpredictable

Math.random() is a fast pseudorandom generator. Its output is spread evenly enough for a game or a shuffle animation, and it is not unpredictable: given enough consecutive outputs, the internal state can be recovered and every future value computed.

So the question is not "is it random enough" but "would anyone benefit from guessing it". Prize draws, tokens, passwords, anything with money attached — use crypto.getRandomValues, which is designed to resist exactly that and is available in every current browser. This tool always uses it.

Draws with and without repeats

Picking six numbers from 1 to 49 for a lottery is without replacement: each number can appear once. Rolling a die six times is with replacement, and getting the same number twice is normal, not a malfunction.

People routinely misread the second case. In six rolls of a die, the chance that all six faces are different is only about 1.5% — so repeats are the expected outcome, and a generator that avoided them would be the broken one.

Advertisement

Frequently Asked Questions

Is Math.random() good enough for random numbers?
For a game or an animation, yes. For anything anyone would want to rig — a prize draw, a token, a password — no. It is spread evenly but it is predictable: given enough consecutive outputs its internal state can be recovered and every future value computed.
What is modulo bias?
Taking a random value mod n is only uniform when n divides the range exactly. A byte holds 256 values, so byte mod 6 gives four outcomes 43 chances each and two outcomes 42 — the low rolls come up 2.38% too often.
How do you generate an unbiased random number?
Rejection sampling. Take the largest multiple of your range that fits in the random value's space, discard anything at or above it, and draw again. For a range of 6 in a byte you discard values 252 to 255, which costs 1.56% of draws and makes the result exactly uniform.
Why did I get the same number twice?
Because a draw with replacement allows it, and it is far more common than people expect. Rolling a die six times, the chance that all six faces are different is about 1.5% — repeats are the normal outcome. Tick 'no repeats' if you want each number at most once.
Can this generate lottery numbers?
Yes — set the range, set how many, and tick 'no repeats'. It will not improve your odds. Every combination is equally likely, so the only thing picking method changes is how many people you would share a prize with if it came up.
Are the numbers really random?
They come from crypto.getRandomValues, which draws on the operating system's cryptographic random source, and rejection sampling keeps the mapping to your range exactly uniform. Nothing is sent anywhere — the generation happens in your browser.

Related tools in Math & Statistics

Browse all Math & Statistics tools
A handwritten note reading ecech.com resting on the keyboard used to build the site.

Made by one person

ecech. is not a content farm. Every tool here is written and checked by hand, one at a time, by someone who wanted the tool to exist and could not find a version that showed its working.

No accounts and no sign-in, and nothing you type reaches a server — every calculation on this page runs inside your browser. The ads are served by Google and do set their own cookies, which is set out in full on the privacy page. More about the site.