Permalink
Browse files

rnd() should use a half open value range

Before this commit all our random ranges where close "[min; max]"
Old code relied on ranges being half open "[min; max[".

In particular the half open range was required when
selecting random elements from lists.

(Which caused assertion errors when we where accessing
elements one index beyond the last one)
  • Loading branch information...
koraa committed Nov 5, 2015
1 parent 65b5ce5 commit a32ccaf578839a02193ea1af9e1ae5c904aa4a34
Showing with 20 additions and 23 deletions.
  1. +4 −4 inexor/test/util/random.cpp
  2. +16 −19 inexor/util/random.h
@@ -40,7 +40,7 @@ array<range, 5> ranges {
range({-10, 10, 100, 2}),
range({-30000, -29900, 100, 1}),
range({5000, 7000, 100, 1}),
range({300,301, 4, 20})
range({300,302, 4, 20})
};
TEST(DeterministicRandom, XToYRange) {
@@ -98,9 +98,9 @@ TEST(PseudoRandom, XToYRange) {
EXPECT_GE(r0, r.a) << "Expected "
"rnd(" << r.a << ", " << r.z << ") "
"to produce values x >= " << r.a << ".";
EXPECT_LE(r0, r.z) << "Expected "
EXPECT_LT(r0, r.z) << "Expected "
"rnd(" << r.a << ", " << r.z << ") "
"to produce values x <= " << r.z << ".";
"to produce values x < " << r.z << ".";
}
}
@@ -114,7 +114,7 @@ TEST(PseudoRandom, TypeFullRange) {
for (int i=0; i<200000; i++)
dist.insert(rnd_raw<uchar>());
EXPECT_EQ((unsigned int)256, dist.size()) << "Expected "
EXPECT_EQ((unsigned int)255, dist.size()) << "Expected "
"20000 random unsigned chars to contain every "
"possible uchar but could find only "
<< dist.size() << " values";
@@ -39,24 +39,21 @@ namespace random {
/// The type of the seed the deterministic_generator expects
typedef rng_engine::result_type seed_t;
/// Uniform real distribution with a closed range.
///
/// It is like the boost uniform distribution, but while
/// that uses an half open distribution [min..max), this
/// class uses a closed distribution [min..max].
/// that uses an closed distribution [min; max], this
/// class uses a half opened one [min; max).
///
/// This means that the boost one can't return 1.0 if
/// max=1.0, but this one can.
/// This means that the boost one can return 1000 if
/// max=1000, but this one can.
template<typename T>
class uniform_closed_range_real_distribution
: public boost::random::uniform_real_distribution<T> {
class uniform_open_range_int_distribution
: public boost::random::uniform_int_distribution<T> {
public:
explicit uniform_closed_range_real_distribution(T min=0.0, T max=1.0)
: boost::random::uniform_real_distribution<T>(
min,
std::nextafter(
max,
std::numeric_limits<T>::infinity()) ) {}
explicit uniform_open_range_int_distribution(
T min = 0
, T max = std::numeric_limits<T>::max)
: boost::random::uniform_int_distribution<T>(
min, max - 1) {}
};
/// A generic uniform random number distribution that
@@ -67,10 +64,10 @@ namespace random {
/// std::uniform_real_distribution, otherwise
/// std::uniform_int_distribution.
template<typename T>
using uniform_generic_distribution = typename
using uniform_generic_open_range_distribution = typename
std::conditional< std::is_floating_point<T>::value,
uniform_closed_range_real_distribution<T>,
boost::random::uniform_int_distribution<T>
boost::random::uniform_real_distribution<T>,
uniform_open_range_int_distribution<T>
>::type;
/// Generate a random number between Rmin and Rmax
@@ -92,7 +89,7 @@ namespace random {
template<typename T>
T rnd(const T Rmin, const T Rmax) {
// TODO. This could be expensive
uniform_generic_distribution<T> dist(Rmin, Rmax);
uniform_generic_open_range_distribution<T> dist(Rmin, Rmax);
return dist(generator);
}
@@ -129,7 +126,7 @@ namespace random {
T deterministic_rnd(const seed_t seed,
const T Rmin, const T Rmax) {
// TODO: Use a lookup table/cache?
uniform_generic_distribution<T> dist(Rmin, Rmax);
uniform_generic_open_range_distribution<T> dist(Rmin, Rmax);
deterministic_generator.seed(seed);
return dist(deterministic_generator);
}

0 comments on commit a32ccaf

Please sign in to comment.