mirror of
https://github.com/gcc-mirror/gcc.git
synced 2026-05-06 14:59:39 +02:00
libstdc++: Rework P0952 generate_cannonical tests.
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. Reviewed-by: Nathan Myers <ncm@cantrip.org> Reviewed-by: Jonathan Wakely <jwakely@redhat.com>
This commit is contained in:
@@ -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)
|
||||
{
|
||||
if (!std::numeric_limits<T>::is_iec559)
|
||||
return;
|
||||
|
||||
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);
|
||||
|
||||
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,158 +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;
|
||||
}
|
||||
rms = std::sqrt(devsquare);
|
||||
}
|
||||
if (!std::numeric_limits<T>::is_iec559)
|
||||
return;
|
||||
|
||||
// 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>
|
||||
void
|
||||
test03(RNG& rng, RNG& rng2,
|
||||
int& deviation, int& max, int& rms, int& zero, int& skips)
|
||||
{
|
||||
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;
|
||||
}
|
||||
rms = std::sqrt(devsquare);
|
||||
}
|
||||
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};
|
||||
|
||||
void
|
||||
test00()
|
||||
{
|
||||
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);
|
||||
int deviation = 0, max = 0, rms = 0, zeros = 0;
|
||||
int skips = run_generator<T, -1u, call_per_elem, 2u>
|
||||
(lrng, deviation, max, rms, zeros);
|
||||
|
||||
{ // 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
|
||||
#if __LDBL_MANT_DIG__ != 106 // disable this for IBM double double
|
||||
int skips{};
|
||||
auto rng2{rng};
|
||||
auto rng3{rng};
|
||||
test01<long double>(rng2, rng3, skips);
|
||||
VERIFY(skips == 0);
|
||||
#endif
|
||||
}
|
||||
#ifndef _GLIBCXX_GENERATE_CANONICAL_STRICT
|
||||
# ifdef __SIZEOF_FLOAT128__
|
||||
switch (mantissa)
|
||||
{
|
||||
int skips{};
|
||||
auto rng2{rng};
|
||||
auto rng3{rng};
|
||||
test01<__float128>(rng2, rng3, skips);
|
||||
VERIFY(skips == 0);
|
||||
}
|
||||
# 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);
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
{ // 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);
|
||||
// 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
|
||||
test_2p31m1(const std::mt19937& rng)
|
||||
{
|
||||
if (!std::numeric_limits<T>::is_iec559)
|
||||
return;
|
||||
|
||||
if (std::numeric_limits<double>::is_iec559)
|
||||
{
|
||||
VERIFY(deviation == 7820);
|
||||
VERIFY(max == 240);
|
||||
VERIFY(rms == 950);
|
||||
VERIFY(zero == 0);
|
||||
}
|
||||
VERIFY(skips == 0);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
test00();
|
||||
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);
|
||||
|
||||
#ifndef _GLIBCXX_GENERATE_CANONICAL_STRICT
|
||||
# ifdef __SIZEOF_FLOAT128__
|
||||
test_2p32<__float128>(rng);
|
||||
test_10p6<__float128>(rng);
|
||||
test_2p31m1<__float128>(rng);
|
||||
# endif
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user