Standard Library[lib]

C++ random number: how to generate one in a range

Section
Standard Library [lib]
Standard
C++11
Compilers
MSVC 19.44, Clang 19.1 (MSVC STL), GCC 16.2, Clang 23.1 (libc++)
Published
Length
7 min read
In this answer7 clauses
  1. 1How do you get a random number in a range?
  2. 2How do you generate a random double between 0 and 1?
  3. 3Why do I get the same number every time?
  4. 4What does the seed do?
  5. 5Why not rand() and srand()?
  6. 6How do you shuffle a list or pick a random element?
  7. 7A random_int function to copy

Abstract

To generate a C++ random number, include <random>, seed one std::mt19937 engine from std::random_device, and draw from a std::uniform_int_distribution<int>: one built with (1, 6) gives 1 to 6, both ends included. Create the engine once and reuse it, because a new engine with the same seed starts the same sequence again. For a double from 0 up to 1, use std::uniform_real_distribution<double>. rand() still compiles, but RAND_MAX can be as small as 32767 and rand() % 6 favours the low results.

A tape of seven cells runs in from the left, each cell holding a black bar of a different height; an arrow leads from its end to a large slate-blue die face showing five pips.
Figure 1 — A stream of raw numbers, fitted to a die.

To generate a C++ random number, include <random>, seed one std::mt19937 engine from std::random_device, and ask a std::uniform_int_distribution<int> for a value: built with (1, 6), it gives 1 to 6, both ends included. Create the engine once and reuse it. The older rand() still compiles, but its range can be small and rand() % 6 makes its results uneven.

A die roll, whole:

[Example 1:
#include <iostream>
#include <random>

int main() {
    std::random_device rd;                         // a seed
    std::mt19937 gen(rd());                        // the engine
    std::uniform_int_distribution<int> die(1, 6);  // the range

    std::cout << die(gen) << '\n';  // a number from 1 to 6
}

— end example]

It printed a number from 1 to 6, and running it again can print a different one. std::random_device supplies the seed, the starting value. std::mt19937 is the engine, the random number generator itself: from the seed it makes a long sequence of large numbers. The distribution, die, fits each of those into 1 to 6, and die(gen) asks it for the next one.

This answer is written for C++11, the standard that added <random> (Since When’s C++11 table). The samples were built as C++11 with GCC 16.2 and Clang 23.1 (libc++), and as C++17 with those two, MSVC 19.44 and Clang 19.1 (MSVC STL); MSVC has no C++11 mode. All four printed the same unless a sentence says otherwise.

How do you get a random number in a range?

Give std::uniform_int_distribution the lowest and the highest value you want: both are included, so (1, 10) gives 1 to 10 and (0, 99) gives 0 to 99. The first may not be larger than the second. Here are 10,000 draws from 1 to 10, with a fixed seed so that every run draws the same numbers:

[Example 2:
std::mt19937 gen(42);  // a fixed seed: the same draws on every run
std::uniform_int_distribution<int> dist(1, 10);  // 1 to 10
// ...
for (int i = 0; i < 10000; ++i) {
    int n = dist(gen);
    smallest = std::min(smallest, n);
    largest = std::max(largest, n);
}

— end example]

All four printed:

[Example 3:
smallest: 1
largest: 10

— end example]

Three boxes stacked, with arrows down: std::random_device rd gives a seed once; std::mt19937 gen, the engine seeded from it, makes large numbers; a uniform_int_distribution of int named die fits each into 1 to 6. Calling die(gen) gives 4, one result.
Figure 2 — Seed once, keep one engine, and let a distribution fit its numbers to your range.

How do you generate a random double between 0 and 1?

Use std::uniform_real_distribution<double> with the same engine: built with (0.0, 1.0), it gives a value that is at least 0.0 and less than 1.0. Unlike the integer distribution, its range leaves the upper end out.

[Example 4:
std::mt19937 gen(42);
std::uniform_real_distribution<double> dist(0.0, 1.0);

std::cout << dist(gen) << '\n';  // one decimal number

— end example]

In the 10,000 draws the sample made after that one, every value was at least 0.0 and less than 1.0 on all four. For another range, change the two arguments: (0.0, 100.0) gives 0.0 up to, but not including, 100.0.

Why do I get the same number every time?

You get the same number every time when a function creates a new engine with the same seed on each call: a new engine starts its sequence from the beginning, so the function returns that sequence’s first number again and again. Create one engine and pass it to the code that needs it:

[Example 5:
int roll_wrong() {
    std::mt19937 gen(42);  // a new engine on every call
    std::uniform_int_distribution<int> die(1, 6);
    return die(gen);       // so always the first roll of seed 42
}

int roll(std::mt19937& gen) {  // the caller keeps one engine
    std::uniform_int_distribution<int> die(1, 6);
    return die(gen);
}

— end example]

Called five times, roll_wrong() printed one number five times on all four. roll() takes the engine by reference, so each call draws the next number from the same one: given a gen created once in main, its five rolls were not all equal.

What does the seed do?

The seed is the engine’s starting point: the same seed gives the same sequence. std::random_device normally gives a different seed on each run, so the numbers change; where the library has no source of real randomness it may repeat, as it did with MinGW’s GCC on Windows before GCC 9.2. A fixed number, std::mt19937 gen(42);, repeats the same numbers on every run, which is what a test or a bug report needs. Calling an engine directly, a(), returns its next raw number:

[Example 6:
std::mt19937 a(42);
std::mt19937 b(42);  // the same seed
for (int i = 0; i < 3; ++i) {
    std::cout << a() << ' ' << b() << '\n';  // equal pairs
}

std::mt19937 gen(42);
std::uniform_int_distribution<int> die(1, 6);
std::cout << "rolls:";
for (int i = 0; i < 5; ++i) {
    std::cout << ' ' << die(gen);
}
std::cout << '\n';

— end example]

The raw numbers were identical on all four (1608637542, 3421126067, 4083286876), because the standard fixes what std::mt19937 produces ([rand.predef]). The rolls were not: 3 5 6 2 5 with GCC’s and Microsoft’s libraries, 4 5 3 5 5 with libc++. So a fixed seed repeats the rolls on one compiler and library; do not expect the same rolls from another.

Why not rand() and srand()?

rand() still builds and older tutorials teach it, but its range can be small, % can make its results uneven, and its state is hidden, so <random> replaces it. The old way, from <cstdlib> and <ctime>:

[Example 7:
std::srand(static_cast<unsigned>(std::time(nullptr)));  // seed
int roll = std::rand() % 6 + 1;                          // 1 to 6

— end example]

srand() seeds it, here from the clock, and each rand() returns the next number, from 0 to RAND_MAX. The three problems:

  • The range. The standard only promises that RAND_MAX is at least 32,767. The sample printed 32767 on the Windows builds, with Microsoft’s library (Microsoft’s rand page gives the same value), and 2147483647 on the Linux builds.
  • Uneven results. rand() % n folds every value rand() can return onto 0 to n - 1, and unless n divides the count of those values evenly, the low results get one extra. Over every value from 0 to 32767, value % 20000 produced 12,768 of its results twice and the other 7,232 once: 0 to 12767 come up twice as often as the rest.
  • One hidden sequence. rand() keeps its state out of sight, so any other code that calls it moves your sequence on, and whether two threads may call it at once is up to the implementation.

How do you shuffle a list or pick a random element?

Shuffle with std::shuffle from <algorithm>, passing the same engine; to pick one element, draw an index from 0 to size() - 1. The players are in a std::vector:

[Example 8:
std::mt19937 gen(42);
std::vector<std::string> players = {"Ana", "Ben", "Chen", "Dara"};

std::shuffle(players.begin(), players.end(), gen);
// ...
std::uniform_int_distribution<std::size_t> pick(
    0, players.size() - 1);
std::string chosen = players[pick(gen)];

— end example]

On all four the names came out in a new order and the pick was one of the four, though each of the three libraries shuffled them into a different order from the same seed. std::shuffle makes every order equally likely and needs random-access iterators, which a vector has. std::random_shuffle, found in older code, was removed from the standard in C++17; use std::shuffle.

A random_int function to copy

Put the engine in a static variable inside a function: it is created on the first call and kept for every call after it.

[Example 9:
int random_int(int low, int high) {
    static std::mt19937 gen{std::random_device{}()};  // made once
    std::uniform_int_distribution<int> dist(low, high);
    return dist(gen);
}

— end example]

1,000 calls of random_int(1, 100) all returned 1 to 100 on all four. Every call draws from the one engine, so call it from one thread at a time: two threads drawing from one engine at once is a data race, which is undefined behavior.

Before you ship code that draws random numbers:

  • One engine, created once (static, or passed by reference), and one per thread if threads draw numbers.
  • Seed it once: from std::random_device, or with a fixed number when a test needs the same numbers.
  • A distribution for the range, not %.
  • Not for passwords, tokens or keys: none of these engines is cryptographically secure.

The rest of the library’s containers and tools are in Standard library.

Questions

How do you generate a random number between 1 and 10 in C++?

Create one std::mt19937 engine seeded from std::random_device, then a std::uniform_int_distribution<int> dist(1, 10), and call dist(gen) for each number. Both ends are included: over 10,000 draws from a fixed seed, the smallest was 1 and the largest 10 with MSVC, GCC and Clang.

What is the difference between rand() and srand()?

srand() sets the seed of the C library's random number generator, usually once at the start of the program; rand() returns the next number of its sequence, from 0 to RAND_MAX. Without srand(), rand() behaves as if srand(1) had been called, so the program gets the same numbers on every run. Both are declared in <cstdlib>.

Is rand() bad in C++?

It works for a toy, but the standard guarantees nothing about its quality, RAND_MAX can be as small as 32767 (it is with Microsoft's library), and rand() % 6 favours the low results. cppreference recommends the C++11 facilities of <random> instead.

Is std::mt19937 safe for passwords or tokens?

No. None of the engines in <random> is cryptographically secure, so do not use them for passwords, tokens or keys. Use a cryptography library's generator for those, such as OpenSSL's RAND_bytes.

Does the same seed give the same numbers on every compiler?

The engine's own numbers, yes: std::mt19937 seeded with 42 gave the same raw values with MSVC, GCC and Clang, because the standard fixes what it produces. The die rolls, not always: five rolls of uniform_int_distribution<int>(1, 6) from seed 42 were 3 5 6 2 5 with GCC's and Microsoft's libraries and 4 5 3 5 5 with libc++. Expect repeatable rolls on one compiler and library only.

How do you generate a random float?

Use std::uniform_real_distribution<float> with the same engine: the standard promises that one built with (0.0f, 1.0f) gives a float that is at least 0.0 and less than 1.0, but rounding can land a float exactly on the upper end on a rare draw (a handful of 268 million did with libc++). Unless you need a float, use std::uniform_real_distribution<double>.

Read next

  1. C++ getline: how to read a whole line of inputStandard Library · Miriam Castellanos · written for C++11
  2. std::expected in C++23: return a value or an errorStandard Library · Miriam Castellanos · written for C++23
  3. C++ queue: how std::queue works, with examplesStandard Library · Miriam Castellanos · written for C++11