C++11 cross compiler/standard library random distribution reproducibility

Unfortunately, as of N3936 (C++14 final draft), no standard provided random distribution has a requirement like this. And it’s easy to see why. There are many valid ways of writing a distribution function. Some better than others. And algorithms for even something as basic as the normal distribution are getting better and are the subject of active research. Mandating use of a single one would unnecessarily roadblock the implementation of future algorithms.

Fortunately, you can write your own. The specification for the headers of the various distribution classes are located under ยง26.5.8. But there is no reason why yours needs to follow this structure necessarily.

(Please note, I have not tested this code thoroughly, and there might be bad behavior with certain engines, or with overflow, though I have taken some pains to avoid the latter, this is intended more as an illustrative example than a canonical source of awesome uniform distribution. That being said, if you find anything wrong with it, let me know in the comments and I’ll be happy to correct it.)

#include <random>
#include <tuple>
#include <iostream>

template<class IntType = int>
class my_uniform_int_distribution {
public:
  // types
  typedef IntType result_type;
  typedef std::pair<int, int> param_type;

  // constructors and reset functions
  explicit my_uniform_int_distribution(IntType a = 0, IntType b = std::numeric_limits<IntType>::max());
  explicit my_uniform_int_distribution(const param_type& parm);
  void reset();

  // generating functions
  template<class URNG>
    result_type operator()(URNG& g);
  template<class URNG>
    result_type operator()(URNG& g, const param_type& parm);

  // property functions
  result_type a() const;
  result_type b() const;
  param_type param() const;
  void param(const param_type& parm);
  result_type min() const;
  result_type max() const;

private:
  typedef typename std::make_unsigned<IntType>::type diff_type;

  IntType lower;
  IntType upper;
};

template<class IntType>
my_uniform_int_distribution<IntType>::my_uniform_int_distribution(IntType a, IntType b) {
  param({a, b});
}

template<class IntType>
my_uniform_int_distribution<IntType>::my_uniform_int_distribution(const param_type& parm) {
  param(parm);
}

template<class IntType>
void my_uniform_int_distribution<IntType>::reset() {}

template<class IntType>
template<class URNG>
auto my_uniform_int_distribution<IntType>::operator()(URNG& g) -> result_type {
  return operator()(g, param());
}

template<class IntType>
template<class URNG>
auto my_uniform_int_distribution<IntType>::operator()(URNG& g, const param_type& parm) -> result_type {
  diff_type diff = (diff_type)parm.second - (diff_type)parm.first + 1;
  if (diff == 0) // If the +1 overflows we are using the full range, just return g()
    return g();

  diff_type badDistLimit = std::numeric_limits<diff_type>::max() / diff;
  do {
    diff_type generatedRand = g();

    if (generatedRand / diff < badDistLimit)
      return (IntType)((generatedRand % diff) + (diff_type)parm.first);
  } while (true);
}

template<class IntType>
auto my_uniform_int_distribution<IntType>::a() const -> result_type {
  return lower;
}

template<class IntType>
auto my_uniform_int_distribution<IntType>::b() const -> result_type {
  return upper;
}

template<class IntType>
auto my_uniform_int_distribution<IntType>::param() const -> param_type {
  return {lower, upper};
}

template<class IntType>
void my_uniform_int_distribution<IntType>::param(const param_type& parm) {
  std::tie(lower, upper) = parm;
  if (upper < lower)
    throw std::exception();
}

template<class IntType>
auto my_uniform_int_distribution<IntType>::min() const -> result_type {
  return lower;
}

template<class IntType>
auto my_uniform_int_distribution<IntType>::max() const -> result_type {
  return upper;
}

int main() {
  std::mt19937 foo;
  my_uniform_int_distribution<int> bar(0,1000);

  for (int i=0; i<99; ++i) {
    bar(foo);
  }

  std::cout << bar(foo) << std::endl;

  return 0;
}

This code prints out 490 on all platforms I’ve tested.

Leave a Comment