This expands on the changes from test fix r16-6710-gda5a5c55284969:
* test name now reflect the size of the generator range,
* extracted code repeated between tests was exctracted to run_generator,
* expanded non-power of two ranges types to cover all IEC559 floating point,
* select values to test based on the size of mantisa instead of type,
handling different long double representations.
The test now cover the cases, where mutliple value greater than one are
produced (and skipped) in the row. To avoid test running infinite loop,
the number of skips per element is limited by max_skips_per_elem template
parameter of run_generator.
The values checked in test_2p31m1<double> differs from their old test03<double>
counterpart, as we now request mantissa - 5 bits for each type (48bits for
ieee64) instead of previously hardoced 30bits.
libstdc++-v3/ChangeLog:
*
testsuite/26_numerics/random/uniform_real_distribution/operators/gencanon.cc:
Updated tests.
---
Cleaned up while waiting for other patch build to finish.
The gencanon.cc test passed in all supported standard modes, and
with/without -m32. OK for trunk?
.../operators/gencanon.cc | 326 +++++++++---------
1 file changed, 166 insertions(+), 160 deletions(-)
diff --git
a/libstdc++-v3/testsuite/26_numerics/random/uniform_real_distribution/operators/gencanon.cc
b/libstdc++-v3/testsuite/26_numerics/random/uniform_real_distribution/operators/gencanon.cc
index de3b0213cf5..ed4348fe4f4 100644
---
a/libstdc++-v3/testsuite/26_numerics/random/uniform_real_distribution/operators/gencanon.cc
+++
b/libstdc++-v3/testsuite/26_numerics/random/uniform_real_distribution/operators/gencanon.cc
@@ -15,16 +15,11 @@ struct local_rng : std::mt19937
static constexpr std::uint64_t min() { return 0; }
static constexpr std::uint64_t max() { return max_val; }
std::uint64_t operator()()
- { return static_cast<std::mt19937&>(*this)() % (max() + 1); }
+ { return std::mt19937::operator()() % (max() + 1); }
+
local_rng(std::mt19937 const& arg) : std::mt19937(arg) {}
};
-// Verify P0952R2 implementation requiring a second round-trip
-// if first yields exactly 1. In this test, the RNG delivering
-// 32 bits per call is seeded such that this occurs once on the
-// sixth iteration for float, and not at all for double.
-// However, each double iteration requires two calls to the RNG.
-
template<typename T>
int ifloor(T t)
{ return static_cast<int>(std::floor(t)); }
@@ -34,39 +29,40 @@ int ifloor(__float128 t)
{ return static_cast<int>(t); }
#endif
-template <typename T, typename RNG>
-void
-test01(RNG& rng, RNG& rng2, int& skips)
+// Verify P0952R2 implementation requiring a second round-trip
+// if first yields exactly 1.
+template<typename T, size_t bits,
+ size_t call_per_elem, size_t max_skips_per_elem,
+ typename RNG>
+int run_generator(const RNG& rng, int& deviation, int& max, int& rms, int&
zeros)
{
- constexpr size_t mantissa = std::numeric_limits<T>::digits;
- constexpr size_t call_per_elem = (mantissa + 31) / 32;
-
- const auto size = 1000000, buckets = 100;
+ constexpr int iters = 1000000, buckets = 100;
std::array<int, buckets> histo{};
- int zero = 0;
- for (auto i = 0; i != size; ++i) {
- T sample = std::generate_canonical<T, -1u>(rng);
+
+ RNG rng1(rng), rng2(rng);
+ int skips = 0;
+ for (auto i = 0; i != iters; ++i) {
+ T sample = std::generate_canonical<T, bits>(rng1);
VERIFY(sample >= T(0.0));
VERIFY(sample < T(1.0)); // libstdc++/64351
if (sample == T(0.0)) {
- ++zero;
+ ++zeros;
}
auto bucket = ifloor(sample * buckets);
++histo[bucket];
rng2.discard(call_per_elem);
- if (rng != rng2) {
- ++skips;
+ for (int j = 0; j < max_skips_per_elem; ++j) {
+ if (rng1 == rng2)
+ break;
rng2.discard(call_per_elem);
- VERIFY(rng == rng2);
+ ++skips;
}
+ VERIFY(rng1 == rng2);
}
- if (!std::numeric_limits<T>::is_iec559)
- return;
-
- int deviation = 0, max = 0, rms = 0, devsquare = 0;
+ int devsquare = 0;
+ const int expected = iters / buckets;
for (int i = 0; i < buckets; ++i) {
- const auto expected = size / buckets;
auto count = histo[i];
auto diff = count - expected;
if (diff < 0) diff = -diff;
@@ -76,26 +72,51 @@ test01(RNG& rng, RNG& rng2, int& skips)
}
rms = std::sqrt(devsquare);
+ return skips;
+}
+
+// In this test, the RNG delivering 32 bits per call is seeded
+// such that this occurs once on the sixth iteration for float,
+// and not at all for double and other types requiring two
+// calls to the RNG.
+template <typename T>
+void
+test_2p32(const std::mt19937& rng)
+{
+ constexpr size_t mantissa = std::numeric_limits<T>::digits;
+ constexpr size_t call_per_elem = (mantissa + 31) / 32;
+
+ int deviation = 0, max = 0, rms = 0, zeros = 0;
+ int skips = run_generator<T, -1u, call_per_elem, 1u>
+ (rng, deviation, max, rms, zeros);
+ if (call_per_elem == 1)
+ VERIFY(skips == 1);
+ else // would require constitutive elements with all bits set
+ VERIFY(skips == 0);
+
+ if (!std::numeric_limits<T>::is_iec559)
+ return;
+
switch (mantissa)
{
case 24: // ieee32
VERIFY(deviation == 7032);
VERIFY(max == 276);
VERIFY(rms == 906);
- VERIFY(zero == 0);
+ VERIFY(zeros == 0);
break;
case 53: // ieee64
case 64: // ieee80
VERIFY(deviation == 7650);
VERIFY(max == 259);
VERIFY(rms == 975);
- VERIFY(zero == 0);
+ VERIFY(zeros == 0);
break;
case 113: // ieee128
VERIFY(deviation == 9086);
VERIFY(max == 290);
VERIFY(rms == 1142);
- VERIFY(zero == 0);
+ VERIFY(zeros == 0);
break;
default:
VERIFY(false);
@@ -103,156 +124,141 @@ test01(RNG& rng, RNG& rng2, int& skips)
}
}
-// This one is for use with local_rng
-template <typename T, typename RNG>
+// Uses a generate that emits range of size 10^6.
+// The elements are multiplied and then dividided,
+// skips are more common.
+template <typename T>
void
-test02(RNG& rng, RNG& rng2,
- int& deviation, int& max, int& rms, int& zero, int& skips)
+test_10p6(const std::mt19937& rng)
{
- const auto size = 1000000, buckets = 100;
- std::array<int, buckets> histo{};
- for (auto i = 0; i != size; ++i) {
- T sample = std::generate_canonical<T, -1u>(rng);
- VERIFY(sample >= T(0.0));
- VERIFY(sample < T(1.0)); // libstdc++/64351
- if (sample == T(0.0)) {
- ++zero;
- }
- auto bucket = static_cast<int>(std::floor(sample * buckets));
- ++histo[bucket];
- rng2.discard(2);
- if (rng != rng2) {
- ++skips;
- rng2.discard(2);
- VERIFY(rng == rng2);
- }
- }
- int devsquare = 0;
- for (int i = 0; i < buckets; ++i) {
- const auto expected = size / buckets;
- auto count = histo[i];
- auto diff = count - expected;
- if (diff < 0) diff = -diff;
- deviation += diff;
- devsquare += diff * diff;
- if (diff > max) max = diff;
+ if (!std::numeric_limits<T>::is_iec559)
+ return;
+
+ constexpr size_t mantissa = std::numeric_limits<T>::digits;
+ static_assert(mantissa < 120);
+ constexpr size_t call_per_elem = (mantissa / 20) + 1;
+ const local_rng<999999ULL> lrng{rng};
+
+ int deviation = 0, max = 0, rms = 0, zeros = 0;
+ int skips = run_generator<T, -1u, call_per_elem, 2u>
+ (lrng, deviation, max, rms, zeros);
+
+ switch (mantissa)
+ {
+ case 24: // ieee32
+ VERIFY(skips == 18);
+ VERIFY(deviation == 8146);
+ VERIFY(max == 250);
+ VERIFY(rms == 1021);
+ VERIFY(zeros == 0);
+ break;
+ case 53: // ieee64
+ VERIFY(skips == 211);
+ VERIFY(deviation == 7492);
+ VERIFY(max == 235);
+ VERIFY(rms == 921);
+ VERIFY(zeros == 0);
+ break;
+ case 64: // ieee80
+ VERIFY(skips == 1);
+ VERIFY(deviation == 7774);
+ VERIFY(max == 250);
+ VERIFY(rms == 958);
+ VERIFY(zeros == 0);
+ break;
+ case 113: // ieee128
+ VERIFY(skips == 3074);
+ VERIFY(deviation == 7568);
+ VERIFY(max == 282);
+ VERIFY(rms == 1001);
+ VERIFY(zeros == 0);
+ break;
+ default:
+ VERIFY(false);
+ break;
}
- rms = std::sqrt(devsquare);
}
-// This one is for the edge-case local_rng. It takes a bit count
-// to use that is smaller than the floating point mantissa's.
-template <typename T, unsigned bits, typename RNG>
+// Uses a generate that emits range of size 2^31-1.
+// It takes a bit count to use that is smaller than the floating point
mantissa's.
+template <typename T>
void
-test03(RNG& rng, RNG& rng2,
- int& deviation, int& max, int& rms, int& zero, int& skips)
+test_2p31m1(const std::mt19937& rng)
{
- const auto size = 1000000, buckets = 100;
- std::array<int, buckets> histo{};
- for (auto i = 0; i != size; ++i) {
- T sample = std::generate_canonical<T, bits>(rng);
- VERIFY(sample >= T(0.0));
- VERIFY(sample < T(1.0)); // libstdc++/64351
- if (sample == T(0.0)) {
- ++zero;
- }
- auto bucket = static_cast<int>(std::floor(sample * buckets));
- ++histo[bucket];
- rng2.discard(2);
- if (rng != rng2) {
- ++skips;
- rng2.discard(2);
- VERIFY(rng == rng2);
- }
- }
- int devsquare = 0;
- for (int i = 0; i < buckets; ++i) {
- const auto expected = size / buckets;
- auto count = histo[i];
- auto diff = count - expected;
- if (diff < 0) diff = -diff;
- deviation += diff;
- devsquare += diff * diff;
- if (diff > max) max = diff;
+ if (!std::numeric_limits<T>::is_iec559)
+ return;
+
+ constexpr size_t mantissa = std::numeric_limits<T>::digits;
+ constexpr size_t bits = mantissa - 5;
+ static_assert(bits < 124);
+ constexpr size_t call_per_elem = (bits / 31) + 1;
+ const local_rng<0x07ffffffeULL> lrng{rng};
+
+ int deviation = 0, max = 0, rms = 0, zeros = 0;
+ int skips = run_generator<T, bits, call_per_elem, 6u>
+ (lrng, deviation, max, rms, zeros);
+
+ switch (mantissa)
+ {
+ case 24: // ieee32
+ VERIFY(skips == 215);
+ VERIFY(deviation == 7624);
+ VERIFY(max == 217);
+ VERIFY(rms == 933);
+ VERIFY(zeros == 1);
+ break;
+ case 53: // ieee64
+ VERIFY(skips == 62);
+ VERIFY(deviation == 7698);
+ VERIFY(max == 234);
+ VERIFY(rms == 937);
+ VERIFY(zeros == 0);
+ break;
+ case 64: // ieee80
+ VERIFY(skips == 143342);
+ VERIFY(deviation == 7788);
+ VERIFY(max == 296);
+ VERIFY(rms == 977);
+ VERIFY(zeros == 0);
+ break;
+ case 113: // ieee128
+ VERIFY(skips == 8);
+ VERIFY(deviation == 8824);
+ VERIFY(max == 334);
+ VERIFY(rms == 1086);
+ VERIFY(zeros == 0);
+ break;
+ default:
+ VERIFY(false);
+ break;
}
- rms = std::sqrt(devsquare);
}
-void
-test00()
+int main()
{
std::mt19937 rng(8890);
std::seed_seq sequence{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
rng.seed(sequence);
rng.discard(12 * 629143);
+
+ test_2p32<float>(rng);
+ test_10p6<float>(rng);
+ test_2p31m1<float>(rng);
+
+ test_2p32<double>(rng);
+ test_10p6<double>(rng);
+ test_2p31m1<double>(rng);
+
+ test_2p32<long double>(rng);
+ test_10p6<long double>(rng);
+ test_2p31m1<long double>(rng);
- { // float
- int skips{};
- auto rng2{rng};
- auto rng3{rng};
- test01<float>(rng2, rng3, skips);
- VERIFY(skips == 1);
- }
- { // double
- int skips{};
- auto rng2{rng};
- auto rng3{rng};
- test01<double>(rng2, rng3, skips);
- VERIFY(skips == 0);
- }
- { // long double
- int skips{};
- auto rng2{rng};
- auto rng3{rng};
- test01<long double>(rng2, rng3, skips);
- VERIFY(skips == 0);
- }
#ifndef _GLIBCXX_GENERATE_CANONICAL_STRICT
# ifdef __SIZEOF_FLOAT128__
- {
- int skips{};
- auto rng2{rng};
- auto rng3{rng};
- test01<__float128>(rng2, rng3, skips);
- VERIFY(skips == 0);
- }
+ test_2p32<__float128>(rng);
+ test_10p6<__float128>(rng);
+ test_2p31m1<__float128>(rng);
# endif
#endif
-
- { // local RNG, returns [0..999'999)
- int deviation{}, max{}, rms{}, zero{}, skips{};
- local_rng<999999ULL> rng2{rng};
- local_rng<999999ULL> rng3{rng};
- test02<float>(rng2, rng3, deviation, max, rms, zero, skips);
-
- if (std::numeric_limits<float>::is_iec559)
- {
- VERIFY(deviation == 8146);
- VERIFY(max == 250);
- VERIFY(rms == 1021);
- VERIFY(zero == 0);
- }
- VERIFY(skips == 18);
- }
-
- { // local RNG, returns [0..0x0'7fff'fffe)
- int deviation{}, max{}, rms{}, zero{}, skips{};
- local_rng<0x07ffffffeULL> rng2{rng};
- local_rng<0x07ffffffeULL> rng3{rng};
- test03<double, 32u>(rng2, rng3, deviation, max, rms, zero, skips);
-
- if (std::numeric_limits<double>::is_iec559)
- {
- VERIFY(deviation == 7820);
- VERIFY(max == 240);
- VERIFY(rms == 950);
- VERIFY(zero == 0);
- }
- VERIFY(skips == 0);
- }
}
-int main()
-{
- test00();
-}
--
2.52.0