1

I would like to be able to use the hardware random number generator, when available and regardless of intel or amd running the code, with C++ random library:

void randomDeviceBernouilli(double bernoulliParameter, uint64_t trialCount) {
    std::random_device randomDevice;

    std::cout << "operator():" << randomDevice() << std::endl;
    std::cout << "default random_device characteristics:" << std::endl;
    std::cout << "minimum: " << randomDevice.min() << std::endl;
    std::cout << "maximum: " << randomDevice.max() << std::endl;
    std::cout << "entropy: " << randomDevice.entropy() << std::endl;
    std::bernoulli_distribution bernoulliDist(bernoulliParameter);
    uint64_t trueCount{ 0ull };

    for (uint64_t i = 0; i < trialCount; i++)
        if (bernoulliDist(randomDevice))
            trueCount++;
    std::cout << "Success " << trueCount << " out of " << trialCount << std::endl;
    const double successRate = (double)trueCount / (double)trialCount;
    std::cout << "Empirical: " << std::fixed << std::setprecision(8) << std::setw(10) << successRate << " Theoretical: " << bernoulliParameter << std::endl;
}

According to this article, entropy() should have returned 0 on a cpu without RDRAND, such as the i7-2670qm ivy bridge (on which I've tested-RDRAND first appeared in its successor, sandy bridge), but it is always 32 in Visual Studio as documented here. It has been suggested that the absence of a random device may cause operator() to throw an exception, but that does not happen either.

One can use the intrinsic int _rdrand32_step (unsigned int* val) for example but that only draws from a uniform distribution, I need to be able to make use of the distributions available in the C++'s random library.

Also, the code should make use of the hardware generator on an AMD cpu.

What is the proper way to use the hardware random number generator(RDRAND) with the C++'s random library in Visual Studio(2017, 2019)?

Vectorizer
  • 1,076
  • 9
  • 24
  • The [msdn](https://learn.microsoft.com/it-it/cpp/standard-library/random?view=msvc-160) declares that _Although the ISO C++ Standard does not require random_device to be cryptographically secure, in Visual Studio it is implemented to be cryptographically secure._ but it doesn't explain how. – xanatos Jan 02 '21 at 18:42
  • Under the hood the VC++ `random_device` class still uses rand_s, as describer here: https://stackoverflow.com/a/9575747/613130 – xanatos Jan 02 '21 at 18:50
  • @xanatos That question is quite dated, when Bull Mountain (RDRAND) was first introduced, one had to use a library from intel. In 8 years they should have found a way to put that in the standard library one would hope. – Vectorizer Jan 02 '21 at 19:15
  • Checked the Universal C Runtime and it is still using `RtlGenRandom`. [Here](https://stackoverflow.com/a/48876970/613130) two years ago someone took a look at `RtlGenRandom` – xanatos Jan 02 '21 at 19:36

1 Answers1

0

In my opinion you should create your own random number engine that can be used by all the distributions available in <random>.

Something like

#include <exception>
#include <immintrin.h>
#include <iostream>
#include <limits>
#include <ostream>
#include <random>
#include <stdexcept>
#include <string>

using namespace std::string_literals;

class rdrand
{
public:
    using result_type = unsigned int;
    
    static constexpr result_type min() { return std::numeric_limits<result_type>::min(); }
    static constexpr result_type max() { return std::numeric_limits<result_type>::max(); }

    result_type operator()()
    {
        result_type x;
        auto success = _rdrand32_step(&x);

        if (!success)
        {
            throw std::runtime_error("rdrand32_step failed to generate a valid random number");
        }

        return x;
    }
};

int main()
try
{
    auto engine = rdrand();
    auto distribution = std::bernoulli_distribution(0.75);

    // Flip a bias coin with probability of heads = 0.75
    for (auto i = 0; i != 20; ++i)
    {
        auto outcome = distribution(engine);
        std::cout << (outcome ? "heads"s : "tails"s) << std::endl;
    }

    return 0;
}
catch (std::exception const& exception)
{
    std::cerr << "Standard exception thrown with message: \""s << exception.what() << "\""s << std::endl;
}
catch (...)
{
    std::cerr << "Unknown exception thrown"s << std::endl;
}

Whether the rdrand instruction is available should be external to the class. You could add code in main to test for it.

user515430
  • 298
  • 1
  • 3
  • 7