From 28c66fdc5a7329aaad33db611944b832564af4d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Ho=C5=99e=C5=88ovsk=C3=BD?= Date: Sun, 10 Dec 2023 12:10:19 +0100 Subject: [PATCH] Make uniform_floating_point_distribution reproducible By moving to use our `uniform_integer_distribution`, which is reproducible across different platforms, instead of the stdlib one which is not, we can provide reproducible results for `float`s and `double`s. Still no reproducibility for `long double`s, because those are too different across different platforms. --- ...ch_uniform_floating_point_distribution.hpp | 5 +- .../RandomNumberGeneration.tests.cpp | 79 +++++++++++++++++++ 2 files changed, 81 insertions(+), 3 deletions(-) diff --git a/src/catch2/internal/catch_uniform_floating_point_distribution.hpp b/src/catch2/internal/catch_uniform_floating_point_distribution.hpp index 9a52bcc7..a1cdfe43 100644 --- a/src/catch2/internal/catch_uniform_floating_point_distribution.hpp +++ b/src/catch2/internal/catch_uniform_floating_point_distribution.hpp @@ -10,10 +10,10 @@ #define CATCH_UNIFORM_FLOATING_POINT_DISTRIBUTION_HPP_INCLUDED #include +#include #include #include -#include namespace Catch { @@ -76,8 +76,7 @@ class uniform_floating_point_distribution { FloatType m_a, m_b; FloatType m_ulp_magnitude; WidthType m_floats_in_range; - // TODO: we want to eventually replace this distribution with our own for reproducibility - std::uniform_int_distribution m_int_dist; + uniform_integer_distribution m_int_dist; // In specific cases, we can overflow into `inf` when computing the // `steps * g` offset. To avoid this, we don't offset by more than this diff --git a/tests/SelfTest/IntrospectiveTests/RandomNumberGeneration.tests.cpp b/tests/SelfTest/IntrospectiveTests/RandomNumberGeneration.tests.cpp index 5c17fb2b..379f8b23 100644 --- a/tests/SelfTest/IntrospectiveTests/RandomNumberGeneration.tests.cpp +++ b/tests/SelfTest/IntrospectiveTests/RandomNumberGeneration.tests.cpp @@ -16,6 +16,8 @@ #include #include +#include + TEST_CASE("Our PCG implementation provides expected results for known seeds", "[rng]") { Catch::SimplePcg32 rng; SECTION("Default seeded") { @@ -489,3 +491,80 @@ TEMPLATE_TEST_CASE( "uniform_integer_distribution is reproducible", REQUIRE_THAT(generated, Catch::Matchers::RangeEquals(uniform_integer_test_params::expected)); } + + +namespace { + template + struct uniform_fp_test_params; + + template<> + struct uniform_fp_test_params { + // These are exactly representable + static constexpr float lowest = -256.125f; + static constexpr float highest = 385.125f; + // These are just round-trip formatted + static constexpr float expected[] = { 92.56961f, + -23.170044f, + 310.81833f, + -53.023132f, + 105.03287f, + 198.77591f, + -172.72931f, + 51.805176f, + -241.10156f, + 64.66101f, + 212.12509f, + -49.24292f, + -177.1399f, + 245.23679f, + 173.22421f }; + }; + template <> + struct uniform_fp_test_params { + // These are exactly representable + static constexpr double lowest = -234582.9921875; + static constexpr double highest = 261238.015625; + // These are just round-trip formatted + static constexpr double expected[] = { 35031.207052832615, + 203783.3401838024, + 44667.940405848756, + -170100.5877224467, + -222966.7418051684, + 127472.72630072923, + -173510.88209096913, + 97394.16172239158, + 119123.6921592663, + 22595.741022785165, + 8988.68409120926, + 136906.86520606978, + 33369.19104222473, + 60912.7615841752, + -149060.05936760217 }; + }; + +// We need these definitions for C++14 and earlier, but +// GCC will complain about them in newer C++ standards +#if __cplusplus <= 201402L + constexpr float uniform_fp_test_params::expected[]; + constexpr double uniform_fp_test_params::expected[]; +#endif +} // namespace + +TEMPLATE_TEST_CASE( "uniform_floating_point_distribution is reproducible", + "[rng][distribution][floating-point][approvals]", + float, + double ) { + Catch::SimplePcg32 pcg( 0xaabb'aabb ); + + const auto lowest = uniform_fp_test_params::lowest; + const auto highest = uniform_fp_test_params::highest; + Catch::uniform_floating_point_distribution dist( lowest, highest ); + + constexpr auto iters = 15; + std::array generated; + for ( int i = 0; i < iters; ++i ) { + generated[i] = dist( pcg ); + } + + REQUIRE_THAT( generated, Catch::Matchers::RangeEquals( uniform_fp_test_params::expected ) ); +}