mirror of
https://github.com/catchorg/Catch2.git
synced 2024-11-22 05:16:10 +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
|
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.
|
in v2.13.4 so that test cases with similar names are shuffled better.
|
||||||
|
|
||||||
Random generators currently rely on platform's stdlib, specifically
|
Since Catch2 vX.Y.Z the random generators use custom distributions,
|
||||||
the distributions from `<random>`. We thus provide no extra guarantee
|
that should be repeatable across different platforms, with few caveats.
|
||||||
above what your platform does. **Important: `<random>`'s distributions
|
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.**
|
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
|
## Generator interface
|
||||||
|
|
||||||
You can also implement your own generators, by deriving from the
|
You can also implement your own generators, by deriving from the
|
||||||
|
@ -7,7 +7,35 @@
|
|||||||
// SPDX-License-Identifier: BSL-1.0
|
// SPDX-License-Identifier: BSL-1.0
|
||||||
|
|
||||||
#include <catch2/generators/catch_generators_random.hpp>
|
#include <catch2/generators/catch_generators_random.hpp>
|
||||||
|
|
||||||
#include <catch2/internal/catch_context.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/internal/catch_context.hpp>
|
||||||
#include <catch2/generators/catch_generators.hpp>
|
#include <catch2/generators/catch_generators.hpp>
|
||||||
#include <catch2/internal/catch_random_number_generator.hpp>
|
#include <catch2/internal/catch_random_number_generator.hpp>
|
||||||
|
#include <catch2/internal/catch_uniform_integer_distribution.hpp>
|
||||||
#include <random>
|
#include <catch2/internal/catch_uniform_floating_point_distribution.hpp>
|
||||||
|
#include <catch2/internal/catch_unique_ptr.hpp>
|
||||||
|
|
||||||
namespace Catch {
|
namespace Catch {
|
||||||
namespace Generators {
|
namespace Generators {
|
||||||
@ -26,7 +27,7 @@ namespace Detail {
|
|||||||
template <typename Float>
|
template <typename Float>
|
||||||
class RandomFloatingGenerator final : public IGenerator<Float> {
|
class RandomFloatingGenerator final : public IGenerator<Float> {
|
||||||
Catch::SimplePcg32 m_rng;
|
Catch::SimplePcg32 m_rng;
|
||||||
std::uniform_real_distribution<Float> m_dist;
|
Catch::uniform_floating_point_distribution<Float> m_dist;
|
||||||
Float m_current_number;
|
Float m_current_number;
|
||||||
public:
|
public:
|
||||||
RandomFloatingGenerator( Float a, Float b, std::uint32_t seed ):
|
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>
|
template <typename Integer>
|
||||||
class RandomIntegerGenerator final : public IGenerator<Integer> {
|
class RandomIntegerGenerator final : public IGenerator<Integer> {
|
||||||
Catch::SimplePcg32 m_rng;
|
Catch::SimplePcg32 m_rng;
|
||||||
std::uniform_int_distribution<Integer> m_dist;
|
Catch::uniform_integer_distribution<Integer> m_dist;
|
||||||
Integer m_current_number;
|
Integer m_current_number;
|
||||||
public:
|
public:
|
||||||
RandomIntegerGenerator( Integer a, Integer b, std::uint32_t seed ):
|
RandomIntegerGenerator( Integer a, Integer b, std::uint32_t seed ):
|
||||||
@ -68,14 +86,6 @@ public:
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
std::enable_if_t<std::is_integral<T>::value, GeneratorWrapper<T>>
|
std::enable_if_t<std::is_integral<T>::value, GeneratorWrapper<T>>
|
||||||
random(T a, T b) {
|
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>(
|
return GeneratorWrapper<T>(
|
||||||
Catch::Detail::make_unique<RandomIntegerGenerator<T>>(a, b, Detail::getSeed())
|
Catch::Detail::make_unique<RandomIntegerGenerator<T>>(a, b, Detail::getSeed())
|
||||||
);
|
);
|
||||||
|
Loading…
Reference in New Issue
Block a user