7
toZero = rand() % N;

This line of code is giving me Clang-Tidy: rand() has limited randomness. Why is this warning coming up? How can I fix it?

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278

3 Answers3

7

Despite rand() being adequate for very many applications (particularly with extensions like Park-Miller and Bays-Durham shuffling), some code checking tools equate it with a scaled down version of the devil.

Clang-Tidy is warning you that it might not have sufficient properties of randomness for your needs.

Bathsheba
  • 231,907
  • 34
  • 361
  • 483
  • Would it be possible to send me a code snippet where Clang-Tidy doesn't warn me while using rand() ? –  Dec 13 '19 at 20:15
4

Would it be possible to send me a code snippet where Clang-Tidy doesn't warn me while using rand()?

If you are sticking with rand(), you must disable the warning:

toZero = rand() % N; // NOLINT(cert-msc30-c, cert-msc50-cpp)

A better option would be to use the <random> library and functions instead.

One example:

#include <iostream>
#include <random>
#include <type_traits>

// A function to return a seeded random number generator.
inline std::mt19937& generator() {
    // the generator will only be seeded once (per thread) since it's static
    static thread_local std::mt19937 gen(std::random_device{}());
    return gen;
}

// A function to generate integers in the range [min, max]
template<typename T, std::enable_if_t<std::is_integral_v<T>>* = nullptr>
T my_rand(T min, T max) {
    std::uniform_int_distribution<T> dist(min, max);
    return dist(generator());
}

// A function to generate floats in the range [min, max)
template<typename T, std::enable_if_t<std::is_floating_point_v<T>>* = nullptr>
T my_rand(T min, T max) {
    std::uniform_real_distribution<T> dist(min, max);
    return dist(generator());
}

int main() {
    unsigned N = 100;
    std::cout << my_rand(0U, N - 1) << '\n'; // unsigned int:s instead of rand() % N
    std::cout << my_rand(-5., 5.) << '\n';   // double's
}
Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108
2

The function rand() is not a C++ function but a C function. It is from stdlib.h, and defined in ISO/IEC 9899 (The C Programming Language). It does not guarantee a portable level of high randomness. There are many different implementations possible and no proper quality control in the standard. A typical implementation of rand() is:

static int next = (int) time();
int rand(void) {
    next = next * 1103515245 + 12345;
    return((unsigned)(next/65536) % 32768);
}

That is, most of the times, rand() is a PRNG and does not use any entropy at all, except maybe for the initial value of next.

So what you should use is the C++11 random library. Here's how it works:

#include <random>
std::default_random_engine generator;
std::uniform_int_distribution<int> distribution(1, 6);
auto dice = std::bind(distribution, generator);
int roll = dice();

DEMO : https://pastebin.run/pzhw6s36g9mf

Notes: The uniform_int_distribution<int> generates a distribution function that will retrieve numbers from the underlying generator that are evenly distributed between the given range. This is important: Modulo is not an even distribution function. Just doing something like int roll = 1 + nextInt() % 6 is not evenly distributed. (Thanks to Christian Hujer)

STEEL
  • 8,955
  • 9
  • 67
  • 89