mirror of
https://github.com/catchorg/Catch2.git
synced 2024-12-22 11:23:29 +01:00
Make the user-facing random Generators reproducible
Thanks to the new distributions, this is almost trivial change.
This commit is contained in:
parent
28c66fdc5a
commit
ae4fe16b81
11
docs/faq.md
11
docs/faq.md
@ -84,9 +84,14 @@ and it is also generally repeatable across versions, but we might break
|
||||
it from time to time. E.g. we broke repeatability with previous versions
|
||||
in v2.13.4 so that test cases with similar names are shuffled better.
|
||||
|
||||
Random generators currently rely on platform's stdlib, specifically
|
||||
the distributions from `<random>`. We thus provide no extra guarantee
|
||||
above what your platform does. **Important: `<random>`'s distributions
|
||||
Since Catch2 vX.Y.Z the random generators use custom distributions,
|
||||
that should be repeatable across different platforms, with few caveats.
|
||||
For details see the section on random generators in the [Generator
|
||||
documentation](generators.md#random-number-generators-details).
|
||||
|
||||
Before this version, random generators relied on distributions from
|
||||
platform's stdlib. We thus can provide no extra guarantee on top of the
|
||||
ones given by your platform. **Important: `<random>`'s distributions
|
||||
are not specified to be repeatable across different platforms.**
|
||||
|
||||
|
||||
|
@ -189,6 +189,31 @@ TEST_CASE("type conversion", "[generators]") {
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### Random number generators: details
|
||||
|
||||
> This section applies from Catch2 vX.Y.Z. Before that, random generators
|
||||
> were a thin wrapper around distributions from `<random>`.
|
||||
|
||||
All of the `random(a, b)` generators in Catch2 currently generate uniformly
|
||||
distributed number in closed interval \[a; b\]. This is different from
|
||||
`std::uniform_real_distribution`, which should return numbers in interval
|
||||
\[a; b) (but due to rounding can end up returning b anyway), but the
|
||||
difference is intentional, so that `random(a, a)` makes sense. If there is
|
||||
enough interest from users, we can provide API to pick any of CC, CO, OC,
|
||||
or OO ranges.
|
||||
|
||||
Unlike `std::uniform_int_distribution`, Catch2's generators also support
|
||||
various single-byte integral types, such as `char` or `bool`.
|
||||
|
||||
Given the same seed, the output from the integral generators is
|
||||
reproducible across different platforms. For floating point generators,
|
||||
we only promise reproducibility on platforms that obey the IEEE 754
|
||||
standard, and where `float` is 4 bytes and `double` is 8 bytes. We provide
|
||||
no guarantees for `long double`, as the internals of `long double` can
|
||||
vary wildly across different platforms.
|
||||
|
||||
|
||||
## Generator interface
|
||||
|
||||
You can also implement your own generators, by deriving from the
|
||||
|
@ -7,7 +7,35 @@
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
|
||||
#include <catch2/generators/catch_generators_random.hpp>
|
||||
|
||||
#include <catch2/internal/catch_context.hpp>
|
||||
|
||||
std::uint32_t Catch::Generators::Detail::getSeed() { return sharedRng()(); }
|
||||
#include <random>
|
||||
|
||||
namespace Catch {
|
||||
namespace Generators {
|
||||
namespace Detail {
|
||||
std::uint32_t getSeed() { return sharedRng()(); }
|
||||
} // namespace Detail
|
||||
|
||||
struct RandomFloatingGenerator<long double>::PImpl {
|
||||
PImpl( long double a, long double b, uint32_t seed ):
|
||||
rng( seed ), dist( a, b ) {}
|
||||
|
||||
Catch::SimplePcg32 rng;
|
||||
std::uniform_real_distribution<long double> dist;
|
||||
};
|
||||
|
||||
RandomFloatingGenerator<long double>::RandomFloatingGenerator(
|
||||
long double a, long double b, std::uint32_t seed) :
|
||||
m_pimpl(Catch::Detail::make_unique<PImpl>(a, b, seed)) {
|
||||
static_cast<void>( next() );
|
||||
}
|
||||
|
||||
RandomFloatingGenerator<long double>::~RandomFloatingGenerator() =
|
||||
default;
|
||||
bool RandomFloatingGenerator<long double>::next() {
|
||||
m_current_number = m_pimpl->dist( m_pimpl->rng );
|
||||
return true;
|
||||
}
|
||||
} // namespace Generators
|
||||
} // namespace Catch
|
||||
|
@ -11,8 +11,9 @@
|
||||
#include <catch2/internal/catch_context.hpp>
|
||||
#include <catch2/generators/catch_generators.hpp>
|
||||
#include <catch2/internal/catch_random_number_generator.hpp>
|
||||
|
||||
#include <random>
|
||||
#include <catch2/internal/catch_uniform_integer_distribution.hpp>
|
||||
#include <catch2/internal/catch_uniform_floating_point_distribution.hpp>
|
||||
#include <catch2/internal/catch_unique_ptr.hpp>
|
||||
|
||||
namespace Catch {
|
||||
namespace Generators {
|
||||
@ -26,7 +27,7 @@ namespace Detail {
|
||||
template <typename Float>
|
||||
class RandomFloatingGenerator final : public IGenerator<Float> {
|
||||
Catch::SimplePcg32 m_rng;
|
||||
std::uniform_real_distribution<Float> m_dist;
|
||||
Catch::uniform_floating_point_distribution<Float> m_dist;
|
||||
Float m_current_number;
|
||||
public:
|
||||
RandomFloatingGenerator( Float a, Float b, std::uint32_t seed ):
|
||||
@ -44,10 +45,27 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
class RandomFloatingGenerator<long double> final : public IGenerator<long double> {
|
||||
// We still rely on <random> for this specialization, but we don't
|
||||
// want to drag it into the header.
|
||||
struct PImpl;
|
||||
Catch::Detail::unique_ptr<PImpl> m_pimpl;
|
||||
long double m_current_number;
|
||||
|
||||
public:
|
||||
RandomFloatingGenerator( long double a, long double b, std::uint32_t seed );
|
||||
|
||||
long double const& get() const override { return m_current_number; }
|
||||
bool next() override;
|
||||
|
||||
~RandomFloatingGenerator() override; // = default
|
||||
};
|
||||
|
||||
template <typename Integer>
|
||||
class RandomIntegerGenerator final : public IGenerator<Integer> {
|
||||
Catch::SimplePcg32 m_rng;
|
||||
std::uniform_int_distribution<Integer> m_dist;
|
||||
Catch::uniform_integer_distribution<Integer> m_dist;
|
||||
Integer m_current_number;
|
||||
public:
|
||||
RandomIntegerGenerator( Integer a, Integer b, std::uint32_t seed ):
|
||||
@ -68,14 +86,6 @@ public:
|
||||
template <typename T>
|
||||
std::enable_if_t<std::is_integral<T>::value, GeneratorWrapper<T>>
|
||||
random(T a, T b) {
|
||||
static_assert(
|
||||
!std::is_same<T, char>::value &&
|
||||
!std::is_same<T, int8_t>::value &&
|
||||
!std::is_same<T, uint8_t>::value &&
|
||||
!std::is_same<T, signed char>::value &&
|
||||
!std::is_same<T, unsigned char>::value &&
|
||||
!std::is_same<T, bool>::value,
|
||||
"The requested type is not supported by the underlying random distributions from std" );
|
||||
return GeneratorWrapper<T>(
|
||||
Catch::Detail::make_unique<RandomIntegerGenerator<T>>(a, b, Detail::getSeed())
|
||||
);
|
||||
|
Loading…
Reference in New Issue
Block a user