mirror of
https://github.com/catchorg/Catch2.git
synced 2025-08-01 21:05:39 +02:00
Introduce Catch's own RNG based on the PCG family of RNGs
In the future, we will also want to introduce our own `uniform_int_distribution` and `uniform_real_distribution` to get repeatable test runs across different platforms.
This commit is contained in:
@@ -7,6 +7,7 @@
|
||||
*/
|
||||
#include "catch_context.h"
|
||||
#include "catch_common.h"
|
||||
#include "catch_random_number_generator.h"
|
||||
|
||||
namespace Catch {
|
||||
|
||||
@@ -59,4 +60,11 @@ namespace Catch {
|
||||
IContext::~IContext() = default;
|
||||
IMutableContext::~IMutableContext() = default;
|
||||
Context::~Context() = default;
|
||||
|
||||
|
||||
SimplePcg32& rng() {
|
||||
static SimplePcg32 s_rng;
|
||||
return s_rng;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -55,6 +55,9 @@ namespace Catch {
|
||||
}
|
||||
|
||||
void cleanUpContext();
|
||||
|
||||
class SimplePcg32;
|
||||
SimplePcg32& rng();
|
||||
}
|
||||
|
||||
#endif // TWOBLUECUBES_CATCH_CONTEXT_H_INCLUDED
|
||||
|
@@ -7,23 +7,67 @@
|
||||
|
||||
#include "catch_random_number_generator.h"
|
||||
#include "catch_context.h"
|
||||
#include "catch_run_context.h"
|
||||
#include "catch_interfaces_config.h"
|
||||
|
||||
namespace Catch {
|
||||
|
||||
std::mt19937& rng() {
|
||||
static std::mt19937 s_rng;
|
||||
return s_rng;
|
||||
namespace {
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable:4146) // we negate uint32 during the rotate
|
||||
#endif
|
||||
// Safe rotr implementation thanks to John Regehr
|
||||
uint32_t rotate_right(uint32_t val, uint32_t count) {
|
||||
const uint32_t mask = 31;
|
||||
count &= mask;
|
||||
return (val >> count) | (val << (-count & mask));
|
||||
}
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
|
||||
SimplePcg32::SimplePcg32(result_type seed_) {
|
||||
seed(seed_);
|
||||
}
|
||||
|
||||
void seedRng( IConfig const& config ) {
|
||||
if( config.rngSeed() != 0 ) {
|
||||
std::srand( config.rngSeed() );
|
||||
rng().seed( config.rngSeed() );
|
||||
|
||||
void SimplePcg32::seed(result_type seed_) {
|
||||
m_state = 0;
|
||||
(*this)();
|
||||
m_state += seed_;
|
||||
(*this)();
|
||||
}
|
||||
|
||||
void SimplePcg32::discard(uint64_t skip) {
|
||||
// We could implement this to run in O(log n) steps, but this
|
||||
// should suffice for our use case.
|
||||
for (uint64_t s = 0; s < skip; ++s) {
|
||||
static_cast<void>((*this)());
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int rngSeed() {
|
||||
return getCurrentContext().getConfig()->rngSeed();
|
||||
SimplePcg32::result_type SimplePcg32::operator()() {
|
||||
// prepare the output value
|
||||
const uint32_t xorshifted = static_cast<uint32_t>(((m_state >> 18u) ^ m_state) >> 27u);
|
||||
const auto output = rotate_right(xorshifted, m_state >> 59u);
|
||||
|
||||
// advance state
|
||||
m_state = m_state * 6364136223846793005ULL + s_inc;
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
bool operator==(SimplePcg32 const& lhs, SimplePcg32 const& rhs) {
|
||||
return lhs.m_state == rhs.m_state;
|
||||
}
|
||||
|
||||
bool operator!=(SimplePcg32 const& lhs, SimplePcg32 const& rhs) {
|
||||
return lhs.m_state != rhs.m_state;
|
||||
}
|
||||
}
|
||||
|
@@ -7,17 +7,52 @@
|
||||
#ifndef TWOBLUECUBES_CATCH_RANDOM_NUMBER_GENERATOR_H_INCLUDED
|
||||
#define TWOBLUECUBES_CATCH_RANDOM_NUMBER_GENERATOR_H_INCLUDED
|
||||
|
||||
#include <algorithm>
|
||||
#include <random>
|
||||
#include <cstdint>
|
||||
|
||||
namespace Catch {
|
||||
|
||||
struct IConfig;
|
||||
// This is a simple implementation of C++11 Uniform Random Number
|
||||
// Generator. It does not provide all operators, because Catch2
|
||||
// does not use it, but it should behave as expected inside stdlib's
|
||||
// distributions.
|
||||
// The implementation is based on the PCG family (http://pcg-random.org)
|
||||
class SimplePcg32 {
|
||||
using state_type = std::uint64_t;
|
||||
public:
|
||||
using result_type = std::uint32_t;
|
||||
static constexpr result_type min() {
|
||||
return 0;
|
||||
}
|
||||
static constexpr result_type max() {
|
||||
return static_cast<result_type>(-1);
|
||||
}
|
||||
|
||||
std::mt19937& rng();
|
||||
void seedRng( IConfig const& config );
|
||||
unsigned int rngSeed();
|
||||
// Provide some default initial state for the default constructor
|
||||
SimplePcg32():SimplePcg32(0xed743cc4U) {}
|
||||
|
||||
}
|
||||
explicit SimplePcg32(result_type seed_);
|
||||
|
||||
void seed(result_type seed_);
|
||||
void discard(uint64_t skip);
|
||||
|
||||
result_type operator()();
|
||||
|
||||
private:
|
||||
friend bool operator==(SimplePcg32 const& lhs, SimplePcg32 const& rhs);
|
||||
friend bool operator!=(SimplePcg32 const& lhs, SimplePcg32 const& rhs);
|
||||
|
||||
// In theory we also need operator<< and operator>>
|
||||
// In practice we do not use them, so we will skip them for now
|
||||
|
||||
|
||||
std::uint64_t m_state;
|
||||
// This part of the state determines which "stream" of the numbers
|
||||
// is chosen -- we take it as a constant for Catch2, so we only
|
||||
// need to deal with seeding the main state.
|
||||
// Picked by reading 8 bytes from `/dev/random` :-)
|
||||
static const std::uint64_t s_inc = (0x13ed0cc53f939476ULL << 1ULL) | 1ULL;
|
||||
};
|
||||
|
||||
} // end namespace Catch
|
||||
|
||||
#endif // TWOBLUECUBES_CATCH_RANDOM_NUMBER_GENERATOR_H_INCLUDED
|
||||
|
@@ -506,4 +506,16 @@ namespace Catch {
|
||||
else
|
||||
CATCH_INTERNAL_ERROR("No result capture instance");
|
||||
}
|
||||
|
||||
void seedRng(IConfig const& config) {
|
||||
if (config.rngSeed() != 0) {
|
||||
std::srand(config.rngSeed());
|
||||
rng().seed(config.rngSeed());
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int rngSeed() {
|
||||
return getCurrentContext().getConfig()->rngSeed();
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -151,6 +151,8 @@ namespace Catch {
|
||||
bool m_includeSuccessfulResults;
|
||||
};
|
||||
|
||||
void seedRng(IConfig const& config);
|
||||
unsigned int rngSeed();
|
||||
} // end namespace Catch
|
||||
|
||||
#endif // TWOBLUECUBES_CATCH_RUNNER_IMPL_HPP_INCLUDED
|
||||
|
@@ -11,6 +11,7 @@
|
||||
#include "catch_enforce.h"
|
||||
#include "catch_interfaces_registry_hub.h"
|
||||
#include "catch_random_number_generator.h"
|
||||
#include "catch_run_context.h"
|
||||
#include "catch_string_manip.h"
|
||||
#include "catch_test_case_info.h"
|
||||
|
||||
|
Reference in New Issue
Block a user