How To Generate Random Number In C

7 min read

Generating random numbers in C is a common requirement for simulations, games, cryptographic prototypes, and many algorithmic tasks. Day to day, the language provides several ways to obtain pseudo‑random values, ranging from the legacy rand()/srand() pair to the modern <random> facilities introduced in C11. Understanding the strengths and weaknesses of each approach helps you choose the right tool for your project and avoid subtle bugs that can affect reproducibility or security.

Using the Classic rand() and srand() Functions

The simplest method relies on functions declared in <stdlib.h>:

#include 
#include 
#include 

int main(void) {
    /* Seed the generator with the current time */
    srand((unsigned)time(NULL));

    /* Produce a random integer in the range [0, RAND_MAX] */
    int r = rand();
    printf("Random number: %d\n", r);

    /* Scale to a desired range, e.g., 1‑100 */
    int dice = 1 + rand() % 100;
    printf("Dice roll (1‑100): %d\n", dice);

    return 0;
}

Key points

  • srand() initializes the internal state of the pseudo‑random number generator (PRNG). If you call it with the same seed, the subsequent sequence of rand() values will be identical—useful for debugging but problematic if you need varied output each run.
  • time(NULL) provides a changing seed based on the system clock, giving a different sequence on each execution (assuming the program runs at least one second apart).
  • The modulus operator (%) is often used to confine the result to a specific interval, but it introduces modulo bias when RAND_MAX is not a multiple of the desired range. For small ranges the bias is negligible; for larger or security‑sensitive applications it can be significant.

Limitations of the Legacy Approach

  • Period and quality – The implementation of rand() varies between compilers and platforms. Many use a linear congruential generator (LCG) with a relatively short period (often 2^31) and noticeable correlations in successive values.
  • Thread safety – The global state modified by srand() and read by rand() is not thread‑safe. Concurrent calls from multiple threads can corrupt the sequence or produce duplicated numbers.
  • Predictability – Because the algorithm is deterministic and often poorly chosen, an observer who sees a few outputs can predict future values, making it unsuitable for cryptographic purposes.

Given these drawbacks, modern C code should prefer the <random> interface when the compiler supports C11 or later Not complicated — just consistent..

Leveraging the C11 <random> Header

C11 added a generic random number facility modeled after C++’s <random>. It separates the engine (the source of random bits) from the distribution (how those bits are mapped to a desired range). This design eliminates modulo bias and lets you swap engines without changing distribution code.

Basic Example

#define _DEFAULT_SOURCE   /* needed for drand48 on some systems */
#include 
#include 
#include 
#include    /* C11 header */

int main(void) {
    /* Create a seed from a high‑resolution clock if available */
    unsigned seed = (unsigned)time(NULL);
    /* Alternatively, use std::random_device for a non‑deterministic seed */
    // std::random_device rd;
    // unsigned seed = rd();

    /* mt19937 is a 32‑bit Mersenne Twister with a period of 2^19937−1 */
    std::mt19937 engine(seed);

    /* Uniform integer distribution in [1, 100] */
    std::uniform_int_distribution dist(1, 100);

    int r = dist(engine);
    printf("Random number (1‑100): %d\n", r);

    /* Floating‑point distribution in [0.0, 1.Consider this: 0) */
    std::uniform_real_distribution real_dist(0. 0, 1.

    return 0;
}

Explanation of components

  • Engine (std::mt19937) – Generates a stream of high‑quality random bits. The Mersenne Twister is widely used because of its long period and good statistical properties. Other engines include std::default_random_engine (implementation‑defined), std::ranlux24, and std::knuth_b.
  • Seed – Determines the starting point in the engine’s state space. Using std::random_device (when available) provides a non‑deterministic seed derived from hardware entropy, improving unpredictability.
  • Distribution – Objects like std::uniform_int_distribution and std::uniform_real_distribution transform the raw bits into a uniformly distributed value within the requested bounds, without modulo bias.
  • Thread safety – Each engine instance holds its own state, so you can safely create separate engines for different threads without interference.

Choosing an Engine

Engine Period Speed Typical Use
std::mt19937 2^19937−1 Fast General purpose, simulations, games
std::mt19937_64 2^19937−1 (64‑bit output) Fast When 64‑bit random numbers are needed
std::ranlux24 / ranlux48 Very long, luxury levels Slower High‑quality statistical work
std::knuth_b Relatively short Very fast Educational examples, low‑stress tasks

Honestly, this part trips people up more than it should.

For most applications, std::mt19937 seeded with std::random_device (or a

For most applications, std::mt19937 seeded with std::random_device (or a combination of time and random_device for redundancy) is a reliable choice. That said, the selection of an engine should always align with the specific requirements of the task. As an example, cryptographic applications demand engines like std::random_device itself, which provides non-deterministic randomness, though it is typically slower and not suitable for generating large volumes of data. In contrast, simulations or games may prioritize speed and can benefit from the balance of quality and performance offered by std::mt19937.

Beyond Uniform Distributions

While the example focuses on uniform distributions, the <random> library supports a wide range of statistical distributions designed for specialized use cases. g.- std::bernoulli_distribution: Produces boolean outcomes (e., coin flips) with a specified probability of success.
Even so, for example:

  • std::normal_distribution: Generates values following a Gaussian distribution, useful for modeling natural phenomena like measurement errors. - std::poisson_distribution: Models the number of events occurring in a fixed interval, ideal for queueing systems or network traffic analysis.

These distributions abstract away the complexity of transforming raw random bits into meaningful values, ensuring correctness and efficiency Took long enough..

Best Practices

  1. Avoid Modulo Bias: As noted earlier, using % N to constrain values introduces bias, especially when the range of the random number generator is not a multiple of N. Always prefer distributions to enforce bounds.
  2. Seed Strategically: Use std::random_device for unpredictable seeds in security-sensitive contexts. For reproducible results (e.g., testing), use a fixed seed.
  3. Reuse Engines, Not Distributions: Engines are designed to be reused across multiple distribution calls, while distributions are lightweight and can be reinitialized as needed.
  4. Thread Safety: While engines are thread-safe when used by separate instances, sharing a single engine across threads requires external synchronization (e.g., std::mutex).

Performance Considerations

Engines like std::mt19937_64 offer 64-bit outputs for applications requiring larger random integers, albeit with slightly higher memory usage. For high-throughput scenarios, benchmark different engines to balance speed and quality. To give you an idea, std::ranlux24 trades speed for higher statistical rigor, making it suitable for Monte Carlo simulations where accuracy is critical.

Conclusion

The C++ Standard Library’s <random> facilities

The C++ Standard Library’s <random> facilities represent a significant evolution from the legacy rand() and srand() functions, offering a modular, type-safe, and statistically sound framework for stochastic programming. That said, by decoupling entropy sources (engines) from statistical shaping (distributions), the library empowers developers to make explicit, informed trade-offs between performance, quality, and determinism. Whether seeding a cryptographic protocol with std::random_device, driving a physics simulation with std::mt19937_64 and std::normal_distribution, or implementing a weighted loot table via std::discrete_distribution, the components compose cleanly to meet the exact semantic requirements of the domain.

Adopting these tools is not merely a matter of modernization; it is a correctness imperative. On top of that, the subtle biases of modulo arithmetic, the pitfalls of poor seeding, and the non-portability of implementation-defined RAND_MAX are effectively eliminated by the standard’s design. As C++ continues to evolve—with proposals for parallel random number generation and expanded distribution families on the horizon—the foundation laid by <random> ensures that code written today remains dependable, maintainable, and ready for the stochastic challenges of tomorrow.

Not obvious, but once you see it — you'll see it everywhere.

Just Got Posted

Fresh Out

Along the Same Lines

Expand Your View

Thank you for reading about How To Generate Random Number In C. We hope the information has been useful. Feel free to contact us if you have any questions. See you next time — don't forget to bookmark!
⌂ Back to Home