Re: [PATCH v3 2/2] libstdc++: Support any generator (up to 64bit) in generate_canonical [PR119739]

Jonathan Wakely <[email protected]> Mon, 3 Aug 2026 19:02:00 +0100
Newsgroups gmane.comp.gcc.patches,gmane.comp.gcc.libstdc++.devel
Message-ID <[email protected]>
On Tue, 21 Jul 2026 at 14:41 +0200, Tomasz Kamiński wrote:
>This patch splits implementation of generate_canonical_any, into three
>different path depending on bits generated (R), required bits of entropy
>(b), and value k such that R^k > 2^d > R^(k-1):
>* log2(R) >= d: single call of generator is sufficient (k = 1),
>  and computation can be performed in the generator value type (_UIntR);
>* bit_width(R^k) <= 128: computation can be performed using type
>  __detail::_Select_uint_least_t; previous implementation is reused;
>* bit_width(R^k) > 128: the computations required may overflow 128 bit;
>  such generators were previously unsupported, as illustrated by
>  r17-507-g5d17eec7d83445.
>
>This patch implements the support for the last branch, by observing
>that while it is possible for computation to overflow 128 the sum:
>  sum_[0, k) urng() * R^i
>The values of sum, such that (sum / x * 2^d) > 1, are rejected, and
>any value (sum / x) > 2^d can be rejected. As 2^d is guaranteed to fit
>in 128 integer, we can use above to reject the value, before overflow
>will occur.
>
>Firstly, we know that for l = k-1, the value R^l < 2^d, and
>sum_[0, l) urng() * R^i <= R^l (because urng() is always < R),
>always fit in 128bit integer. We compute that value (__lsum) using
>normal loop.
>
>This leaves us with final iteration of multiplying __kth * R^l, where
>__kth is result of invocation of urng. In that case we compute the
>bitwidth (__abits) of value that can be multiplied by _R^l without
>overflow 128bit integer. The __kth * R^l is performed by multiplying
>the loop of __abits of __kth in loop where:
>* __sumx (initially __lsum / __x) is the current value of sum / x,
>* __remx (initially 2^d - __sumx) is the value that we can still,
>  add before overflowing (shifted with each iteration)
>* __modx (initially __modx % __x) are corrected reminders.
>In each iteration the lowest __kth bits are multiplied with _Rl (__val),
>scaled (__valx) and checked against __rem. When fits the __sumx, __modx
>and __remx are updated, and both values of __remx and __kth are shifted
>right by __abits.
>
>If the multiplication finished without overflow (__kth is zero),
>we handle the collected remainders (__modx), and compute the __ret value.
>The value greater or equal 1 (e.g. due floating point approximation), are
>still rejected.
>
>Similar approach is applied when computing x = R^k / 2^d.
>Again while R^k will overflow 128 bits, we know that x < R (so fits
>in 64bits), as R^l < 2^d < R^k, then x < R. We again compute
>R * R^l / 2^d, by splitting the value of R into __abits chunks (_pR).
>For result of chunks multipliations (_pRk), the bits (_xp) remaining
>after division by __shift (starts from __d and reduces by __abits),
>are collected into cumulative value of __x. While the lower bits
>are preserved for later iteration (__pRk).
>
>	PR libstdc++/119739
>
>libstdc++-v3/ChangeLog:
>
>	* include/bits/random.tcc (__generate_canonical_any):
>	Rewrite to support more cases.
>	* testsuite/26_numerics/random/uniform_real_distribution/operators/gencanon.cc:
>	Add test for previously unsupported generator.
>	* testsuite/26_numerics/random/uniform_real_distribution/operators/gencanon_eng.cc:
>	Add test for previously unsuported-generator ranges.

"unsupported"

>	* testsuite/26_numerics/random/uniform_real_distribution/operators/gencanon_eng_neg.cc:
>	Moved (now-supported) generator test to gencanon_eng.cc, and removed
>	the file.
>---
>v3 updates test to cover 106 bit mantisa (ibm 128), and folds
>together mantisa sizes that fits into same multiply of 55.
>
>Testing on x86_64-linux. *gencanon* test passed both on powerpc64 and
>x86-64. OK for trunk?
>
> libstdc++-v3/include/bits/random.tcc          | 167 +++++++++++++++---
> .../operators/gencanon.cc                     |  91 ++++++++++
> .../operators/gencanon_eng.cc                 |  71 +++++++-
> .../operators/gencanon_eng_neg.cc             |  89 ----------
> 4 files changed, 298 insertions(+), 120 deletions(-)
> delete mode 100644 libstdc++-v3/testsuite/26_numerics/random/uniform_real_distribution/operators/gencanon_eng_neg.cc
>
>diff --git a/libstdc++-v3/include/bits/random.tcc b/libstdc++-v3/include/bits/random.tcc
>index aa176db4778..b4353886e45 100644
>--- a/libstdc++-v3/include/bits/random.tcc
>+++ b/libstdc++-v3/include/bits/random.tcc
>@@ -242,7 +242,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>     constexpr size_t
>     mersenne_twister_engine<_UIntType, __w, __n, __m, __r, __a, __u, __d,
> 			    __s, __b, __t, __c, __l, __f>::tempering_u;
>-
>+
>   template<typename _UIntType,
> 	   size_t __w, size_t __n, size_t __m, size_t __r,
> 	   _UIntType __a, size_t __u, _UIntType __d, size_t __s,
>@@ -3807,38 +3807,149 @@ namespace __detail
>       constexpr unsigned __log2R
> 	= sizeof(_UIntR) * __CHAR_BIT__ - __builtin_clzg(__R) - 1;
>       // We overstimate number of required bits, by computing
>-      // r such that l * log2(R) >= d, so:
>-      // R^l >= (2 ^ log2(R)) ^ l == 2 ^ (log2(r) * l) >= 2^d
>-      // And then requiring l * bit_width(R) bits.
>-      constexpr unsigned __l = (__d + __log2R - 1) / __log2R;
>-      constexpr unsigned __bits = (__log2R + 1) * __l;
>-      using _UInt = typename __detail::_Select_uint_least_t<__bits>::type;
>-
>-      _GLIBCXX_GEN_CANON_CONST _UInt __rd = _UInt(1) << __d;
>-      _GLIBCXX_GEN_CANON_CONST auto __logRrd = __gen_canon_log(__rd, __R);
>-      _GLIBCXX_GEN_CANON_CONST unsigned __k
>-	 = __logRrd.__floor_log + (__rd > __logRrd.__floor_pow);
>-
>-      _GLIBCXX_GEN_CANON_CONST _UInt __Rk
>-	 = (__k > __logRrd.__floor_log)
>-	   ? _UInt(__logRrd.__floor_pow) * _UInt(__R)
>-	   : _UInt(__logRrd.__floor_pow);
>-      _GLIBCXX_GEN_CANON_CONST _UInt __x =  __Rk / __rd;
>+      // r such that m * log2(R) >= d, so:
>+      // R^m >= (2 ^ log2(R)) ^ m == 2 ^ (log2(r) * m) >= 2^d
>+      // And then requiring m * bit_width(R) bits.
>+      constexpr unsigned __m = (__d + __log2R - 1) / __log2R;
>+      constexpr unsigned __bits = (__log2R + 1) * __m;
>+      if constexpr (__log2R >= __d)
>+	{
>+	  // range already provide required number of bits,
>+	  // so in this case __k == 1, and single call to generator
>+	  // is sufficient. Futhermore 2^d fits in _UIntR

"Furthermore"

>+	  constexpr _UIntR __rd = _UIntR(1) << __d;
>+	  constexpr _UIntR __x = __R >> __d;
>
>-      while (true)
>+	  while (true)
>+	    {
>+	      _UIntR __val(__urng() - _Urbg::min());
>+	      const _RealT __ret = _RealT(__val / __x) / _RealT(__rd);
>+	      if (__ret < _RealT(1.0))
>+		return __ret;
>+	    }
>+	}
>+      // generator call produce fewer bits than __d, but R^k > rd fits
>+      // into 128 bits.
>+      else if constexpr (__bits <= 128)
> 	{
>-	  _UInt __Ri{1};
>-	  _UInt __sum(__urng() - _Urbg::min());
>-	  for (int __i = __k - 1; __i > 0; --__i)
>+	  using _UInt = typename __detail::_Select_uint_least_t<__bits>::type;
>+
>+	  _GLIBCXX_GEN_CANON_CONST _UInt __rd = _UInt(1) << __d;
>+	  _GLIBCXX_GEN_CANON_CONST auto __logRrd = __gen_canon_log(__rd, __R);
>+	  // __rd is power of two, and __R is not so __floor_pow is never
>+	  // equal to __rd.
>+	  _GLIBCXX_GEN_CANON_CONST unsigned __k = __logRrd.__floor_log + 1;
>+	  _GLIBCXX_GEN_CANON_CONST _UInt __Rk = __logRrd.__floor_pow * _UInt(__R);
>+	  // x = floor(R^k / 2^d) = R^k >> d;
>+	  _GLIBCXX_GEN_CANON_CONST _UInt __x =  __Rk >> __d;
>+
>+	  while (true)
> 	    {
>-	      __Ri *= _UInt(__R);
>-	      __sum += _UInt(__urng() - _Urbg::min()) * __Ri;
>+	      _UInt __Ri{1};
>+	      _UInt __sum(__urng() - _Urbg::min());
>+	      for (int __i = __k - 1; __i > 0; --__i)
>+		{
>+		  __Ri *= __R;
>+		  __sum += __Ri * _UIntR(__urng() - _Urbg::min());
>+		}
>+	      const _RealT __ret = _RealT(__sum / __x) / _RealT(__rd);
>+	      if (__ret < _RealT(1.0))
>+		return __ret;
> 	    }
>-	  const _RealT __ret = _RealT(__sum / __x) / _RealT(__rd);
>-	  if (__ret < _RealT(1.0))
>-	    return __ret;
> 	}
>-#undef _GLIBCXX_GEN_CANON_CONST
>+      else
>+	{
>+      	  static_assert(__log2R < 64, "Only generators emitting up to 64 bits are supported");

Inconsistent formatting on the line above, it looks like leading
spaces before the first TAB.

>+	  using _UInt = typename __detail::_Select_uint_least_t<128>::type;
>+	  using _UInt64 = typename __detail::_Select_uint_least_t<64>::type;
>+
>+	  _GLIBCXX_GEN_CANON_CONST _UInt __rd = _UInt(1) << __d;
>+	  _GLIBCXX_GEN_CANON_CONST auto __logRrd = __gen_canon_log(__rd, __R);
>+	  // __rd is power of two, and __R is not so __floor_pow is never
>+	  // equal to __rd, and l == k - 1
>+	  _GLIBCXX_GEN_CANON_CONST unsigned __l = __logRrd.__floor_log;
>+	  _GLIBCXX_GEN_CANON_CONST _UInt __Rl = __logRrd.__floor_pow;
>+
>+	  // __abits is maximum bit width of the value, that can
>+	  // be multiplied by R^l without overflowing 128 bit integer
>+	  // TODO: use bit_width(_Rl)

I asked an LLM to find problems in this logic and it found that when
__bwRl == 128, __abits = 0, the masks and shifts become no-ops, and
the __x computation loop runs forever:

   R = 129, d = 113 (__float128)

   __log2R = 7
   __m = ceil(113/7) = 17
   __bits = 8 * 17 = 136 > 128  →  path 3

   __gen_canon_log(2^113, 129) = {16, 129^16}
   __l = 16
   __Rl = 129^16  (≈ 2^112.2, fits in 128 bits)

   __bwRl = 8 * 16 = 128
   __abits = 128 - 128 = 0
   __amask = (1ULL << 0) - 1 = 0

   In the __x computation loop:
   __pR = 129, __pRk = 0, __shift = 113

   Iteration 0:
     __pRk += __Rl * (__pR & 0)  →  __pRk += 0  →  __pRk = 0
     __pR >>= 0                  →  __pR = 129    (no-op!)
     __xp = 0 >> 113 = 0
     __x += 0
     __pRk >>= 0                 →  __pRk = 0     (no-op)
     __shift -= 0                →  __shift = 113  (no-op)

   Iteration 1:  identical to iteration 0
   Iteration 2:  identical
   ...  infinite loop, __pR never reaches 0

   The __amask = 0 zeroes every chunk, so __pRk never accumulates
   anything, and __pR >>= 0 never drains the input. The loop condition
   __pR stays 129 forever.

   ---

The TODO above would fix the problem:

   Since __Rl = R^l < 2^d and d ≤ 113 for all IEEE types, the actual
   bit-width of __Rl is at most 113. That guarantees __abits ≥ 128 -
   113 = 15, which is always positive and large enough for the loop to
   make progress.

   For the R = 129 example: bit_width(129^16) = 113 (since 129^16 ≈
   2^{112.2}), giving __abits = 128 - 113 = 15. The loop would run
   ceil(8/15) = 1 iteration and terminate correctly.

   The existing __builtin_clzg could compute this — something like:
   constexpr unsigned __bwRl = 128 - __builtin_clzg(__Rl, 128);

>+	  _GLIBCXX_GEN_CANON_CONST unsigned __bwRl = (__log2R + 1) * __l;
>+	  // For __R close to power of two, the actual __k may be smaller than __m,
>+	  // and will use less than 128bits, default to two 32 bits chunks.
>+	  _GLIBCXX_GEN_CANON_CONST unsigned __abits = __bwRl > 96 ? 128 - __bwRl : 32;
>+	  _GLIBCXX_GEN_CANON_CONST _UInt64 __amask = (_UInt64(1) << __abits) - 1;
>+
>+	  // Compute __x = _Rk / __rd (_Rk >> __d), by spliting _R into chunks
>+	  // (_pR) of __abits, so their multiplication does not overflow 128 bits.
>+	  // __x fits in 64bits, becuse __R^l < __rd < __R^k, thus __x < _R
>+	  _UInt64 __x(0), __pR(__R); _UInt __pRk(0);
>+	  for (unsigned __shift = __d; __pR; __shift -= __abits)
>+	    {
>+	      __pRk += __Rl * (__pR & __amask);
>+	      __pR >>= __abits;
>+
>+	      _UInt __xp(__pRk >> __shift);
>+	      __x += _UInt64(__xp);
>+	      __pRk -= (__xp << __shift);
>+	      __pRk >>= __abits;
>+	    }
>+
>+	  while (true)
>+	    {
>+	      // Compute sum of __urng() * R^i for i in range [0, l).
>+	      // The value is smaller than R^l which is smaller than
>+	      // __rd so fits into 128 bit integer.
>+	      _UInt __Ri{1};
>+	      _UInt __lsum(__urng() - _Urbg::min());
>+	      for (int __i = __l - 1; __i > 0; --__i)
>+		{
>+		  __Ri *= _UInt64(__R);
>+		  __lsum += __Ri * _UInt64(__urng() - _Urbg::min());
>+		}
>+
>+	      // The last iteration __urng() * R^k may overflow 128bit
>+	      // integer. We use the fact that we reject __sum / __x >= __rd,
>+	      // and split __urng() value into chunks of __abits, so their
>+	      // products with R^l does not overflow 128bit. These products
>+	      // are then multiplied by 2^__shift, scaled by __x and added into
>+	      // the __sumx, until their value is smaller than remaining value
>+	      // __rem [(__rd - __sumx) * 2^__shift], or whole value was
>+	      // multiplied (__kth is zero). The remainder of division by
>+	      // __x are accumulated into __modx value, and handled later.
>+	      // In consequence this guarantees that __sumx never exceeds
>+	      // __rd value, and thus does not overflow 128 bit integer.
>+	      _UInt __sumx = __lsum / __x, __modx = __lsum % __x;
>+	      _UInt __remx = __rd - __sumx;
>+	      _UInt64 __kth(__urng() - _Urbg::min());
>+	      for (unsigned __shift = 0; __kth; __shift += __abits)
>+		{
>+		  const _UInt __val = __Rl * (__kth & __amask);
>+		  const _UInt __valx = __val / __x;
>+		  if (__valx > __remx)
>+		    break;
>+		  __kth >>= __abits;
>+
>+		  __sumx += (__valx << __shift);
>+		  __modx += ((__val % __x) << __shift);
>+
>+		  __remx -= __valx;
>+		  __remx >>= __abits;
>+		}
>+	      if (__kth) // (__sum / __x) > _rd after adding __kth iteration
>+		continue;
>+
>+	      // Handle accumulated remainders
>+	      const _UInt __valx = __modx / __x;
>+	      if (__valx > __rd - __sumx) // avoids overflow
>+		continue;
>+
>+	      __sumx += __valx;
>+	      const _RealT __ret = _RealT(__sumx) / _RealT(__rd);
>+	      if (__ret < _RealT(1.0))
>+		return __ret;
>+	    }
>+	  }
>+#undef _GLIBCXX_GEN_CANON_CONST
>     }
>
> #if !defined(_GLIBCXX_GENERATE_CANONICAL_STRICT)
>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 1161d5603c2..8ffc7b9b62a 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
>@@ -249,6 +249,93 @@ test_2p31m1(const std::mt19937& rng)
>   }
> }
>
>+template<std::uint64_t Max, typename Under = std::mt19937_64>
>+struct trimmed_engine
>+{
>+  using result_type = std::uint64_t;
>+
>+  static constexpr
>+  result_type min()
>+  { return result_type(0); }
>+
>+  static constexpr
>+  result_type max()
>+  { return result_type(Max); }
>+
>+  trimmed_engine() : dist(min(), max())
>+  {}
>+
>+  trimmed_engine(const Under& gen)
>+  : under(gen), dist(min(), max())
>+  {}
>+
>+  result_type operator()()
>+  { return dist(under); }
>+
>+  void discard(std::size_t n)
>+  {
>+    for (size_t i = 0; i < n; ++i)
>+      (void)dist(under);
>+  }
>+
>+  friend bool
>+  operator==(const trimmed_engine& lhs, const trimmed_engine& rhs)
>+  { return lhs.under == rhs.under; }
>+
>+private:
>+  Under under;
>+  std::uniform_int_distribution<result_type> dist;
>+};
>+
>+// Uses a generator that emits range of size 2^55+2.
>+// To produce 128bit IEE float, we need 3 calls to above generator,
>+// that can produce value that have 155 bits, and uses separate path.
>+template <typename T>
>+void
>+test_2p55p1(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 / 55) + 1;
>+  const trimmed_engine<(1ULL << 55) + 1ULL, std::mt19937> lrng{rng};
>+
>+  int deviation = 0, max = 0, rms = 0, zeros = 0;
>+  int skips = run_generator<T, -1u, call_per_elem, 1u>
>+		(lrng, deviation, max, rms, zeros);
>+
>+  switch (mantissa)
>+  {
>+  case 24: // ieee32
>+  case 53: // ieee64
>+    VERIFY(skips == 0);
>+    VERIFY(deviation == 7682);
>+    VERIFY(max == 295);
>+    VERIFY(rms == 971);
>+    VERIFY(zeros == 0);
>+    break;
>+  case 64: // ieee80
>+  case 106: // ibm128
>+    VERIFY(skips == 0);
>+    VERIFY(deviation == 6788);
>+    VERIFY(max == 248);
>+    VERIFY(rms == 854);
>+    VERIFY(zeros == 0);
>+    break;
>+  case 113: // ieee128
>+    VERIFY(skips == 0);
>+    VERIFY(deviation == 8222);
>+    VERIFY(max == 257);
>+    VERIFY(rms == 999);
>+    VERIFY(zeros == 0);
>+    break;
>+  default:
>+    VERIFY(false);
>+    break;
>+  }
>+}
>+
> int main()
> {
>   std::mt19937 rng(8890);
>@@ -259,20 +346,24 @@ int main()
>   test_2p32<float>(rng);
>   test_10p6<float>(rng);
>   test_2p31m1<float>(rng);
>+  test_2p55p1<float>(rng);
>
>   test_2p32<double>(rng);
>   test_10p6<double>(rng);
>   test_2p31m1<double>(rng);
>+  test_2p55p1<double>(rng);
>
>   test_2p32<long double>(rng);
>   test_10p6<long double>(rng);
>   test_2p31m1<long double>(rng);
>+  test_2p55p1<long double>(rng);
>
> #ifndef _GLIBCXX_GENERATE_CANONICAL_STRICT
> #  ifdef __SIZEOF_FLOAT128__
>   test_2p32<__float128>(rng);
>   test_10p6<__float128>(rng);
>   test_2p31m1<__float128>(rng);
>+  test_2p55p1<__float128>(rng);
> #  endif
> #endif
> }
>diff --git a/libstdc++-v3/testsuite/26_numerics/random/uniform_real_distribution/operators/gencanon_eng.cc b/libstdc++-v3/testsuite/26_numerics/random/uniform_real_distribution/operators/gencanon_eng.cc
>index 662e3568a30..e765215eae5 100644
>--- a/libstdc++-v3/testsuite/26_numerics/random/uniform_real_distribution/operators/gencanon_eng.cc
>+++ b/libstdc++-v3/testsuite/26_numerics/random/uniform_real_distribution/operators/gencanon_eng.cc
>@@ -10,13 +10,49 @@ test_engine()
>   (void)std::generate_canonical<_Real, size_t(-1)>(__engine);
> }
>
>+template<std::uint64_t Max, typename Under = std::mt19937_64>
>+struct trimmed_engine
>+{
>+  using result_type = std::uint64_t;
>+
>+  static constexpr
>+  result_type min()
>+  { return result_type(0); }
>+
>+  static constexpr
>+  result_type max()
>+  { return result_type(Max); }
>+
>+  trimmed_engine() : dist(min(), max())
>+  {}
>+
>+  result_type operator()()
>+  { return dist(under); }
>+
>+private:
>+  Under under;
>+  std::uniform_int_distribution<result_type> dist;
>+};
>+
>+template<typename Real, size_t Bits>
>+void
>+test_bits()
>+{
>+  trimmed_engine<(std::uint64_t(1) << Bits) - 1> pow2_engine;
>+  (void)std::generate_canonical<Real, -1u>(pow2_engine);
>+  trimmed_engine<(std::uint64_t(1) << Bits) - 2> high_non_pow2_engine;
>+  (void)std::generate_canonical<Real, -1u>(high_non_pow2_engine);
>+  trimmed_engine<(std::uint64_t(1) << (Bits - 1))> low_non_pow2_engine;
>+  (void)std::generate_canonical<Real, -1u>(low_non_pow2_engine);
>+}
>+
> template<typename _Real>
> void
> test_all_engines()
> {
>   test_engine<_Real, std::default_random_engine>();
>
>-  test_engine<_Real, std::minstd_rand0>();
>+  test_engine<_Real, std::minstd_rand0>();
>   test_engine<_Real, std::minstd_rand>();
>   test_engine<_Real, std::mt19937>();
>   test_engine<_Real, std::mt19937_64>();
>@@ -25,10 +61,39 @@ test_all_engines()
>   test_engine<_Real, std::ranlux24>();
>   test_engine<_Real, std::ranlux48>();
>   test_engine<_Real, std::knuth_b>();
>-#if __cplusplus > 202302L
>+#if __cplusplus > 202302L
>   test_engine<_Real, std::philox4x32>();
>   test_engine<_Real, std::philox4x64>();
> #endif
>+
>+  // For 128bit floating points, generator emitting a range, which size is
>+  // not power of two, but of width of B bits, such that for any N:
>+  // N * B < 113 (bits in ieee128)
>+  // (N+1) * B > 128
>+  // use >128bits patch, as they would otherwise require integer with more
>+  // than 128 bits.
>+  // N == 3: B in [43, 57)
>+  test_bits<_Real, 42>(); // 3 calls
>+  test_bits<_Real, 43>(); // 3 calls, >128bits path
>+  test_bits<_Real, 56>(); // 3 calls, >128bits path
>+  test_bits<_Real, 57>(); // 2 calls
>+
>+  // N == 4: B in [33, 38)
>+  test_bits<_Real, 32>(); // 4 calls
>+  test_bits<_Real, 33>(); // 4 calls, >128bits path
>+  test_bits<_Real, 37>(); // 4 calls, >128bits path
>+  test_bits<_Real, 38>(); // 3 calls
>+
>+  // N == 5: B in [26, 29)
>+  test_bits<_Real, 25>(); // 5 calls
>+  test_bits<_Real, 26>(); // 5 calls, >128bits path
>+  test_bits<_Real, 28>(); // 5 calls, >128bits path
>+  test_bits<_Real, 29>(); // 4 calls
>+
>+  // N == 6: B == 22
>+  test_bits<_Real, 21>(); // 6 calls
>+  test_bits<_Real, 22>(); // 6 calls, >128bits path
>+  test_bits<_Real, 23>(); // 5 calls*/

Stray */ above.

> }
>
> int main()
>@@ -40,5 +105,5 @@ int main()
> #  ifdef __SIZEOF_FLOAT128__
>   test_all_engines<__float128>();
> #  endif
>-#endif
>+#endif
> }
>diff --git a/libstdc++-v3/testsuite/26_numerics/random/uniform_real_distribution/operators/gencanon_eng_neg.cc b/libstdc++-v3/testsuite/26_numerics/random/uniform_real_distribution/operators/gencanon_eng_neg.cc
>deleted file mode 100644
>index e3b4540bf2b..00000000000
>--- a/libstdc++-v3/testsuite/26_numerics/random/uniform_real_distribution/operators/gencanon_eng_neg.cc
>+++ /dev/null
>@@ -1,89 +0,0 @@
>-// { dg-do compile { target { c++11 } } }
>-// { dg-require-effective-target __float128 }
>-// { dg-require-effective-target base_quadfloat_support }
>-// { dg-add-options __float128 }
>-
>-#include <random>
>-#include <cstdint>
>-
>-template<std::uint64_t Max, typename Under = std::mt19937_64>
>-struct trimmed_engine
>-{
>-  using result_type = std::uint64_t;
>-
>-  static constexpr
>-  result_type min()
>-  { return result_type(0); }
>-
>-  static constexpr
>-  result_type max()
>-  { return result_type(Max); }
>-
>-  trimmed_engine() : dist(min(), max())
>-  {}
>-
>-  result_type operator()()
>-  { return dist(under); }
>-
>-private:
>-  Under under;
>-  std::uniform_int_distribution<result_type> dist;
>-};
>-
>-template<typename Real, size_t Bits>
>-void
>-test_non_pow2()
>-{
>-  trimmed_engine<(std::uint64_t(1) << Bits) - 2> non_pow2_engine;
>-  (void)std::generate_canonical<Real, -1u>(non_pow2_engine);
>-}
>-
>-template<typename Real, size_t Bits>
>-void
>-test_pow2()
>-{
>-  trimmed_engine<(std::uint64_t(1) << Bits) - 1> pow2_engine;
>-  (void)std::generate_canonical<Real, -1u>(pow2_engine);
>-}
>-
>-int main()
>-{
>-  // For 128bit floating points, generator emitting a range, which size is
>-  // not power of two, but of width of B bits, such that for any N:
>-  // N * B < 113 (bits in ieee128)
>-  // (N+1) * B > 128
>-  // are not supported, as they would require integer with more than 127 bits.
>-
>-  // N == 3: B in [43, 57)
>-  test_non_pow2<__float128, 42>(); // 3 calls
>-  test_non_pow2<__float128, 43>(); // { dg-error "from here" }
>-  test_non_pow2<__float128, 56>(); // { dg-error "from here" }
>-  test_non_pow2<__float128, 57>(); // 2 calls
>-  test_pow2<__float128, 43>();
>-  test_pow2<__float128, 56>();
>-
>-  // N == 4: B in [33, 38)
>-  test_non_pow2<__float128, 32>(); // 4 calls
>-  test_non_pow2<__float128, 33>(); // { dg-error "from here" }
>-  test_non_pow2<__float128, 37>(); // { dg-error "from here" }
>-  test_non_pow2<__float128, 38>(); // 3 calls
>-  test_pow2<__float128, 33>();
>-  test_pow2<__float128, 37>();
>-
>-  // N == 5: B in [26, 29)
>-  test_non_pow2<__float128, 25>(); // 5 calls
>-  test_non_pow2<__float128, 26>(); // { dg-error "from here" }
>-  test_non_pow2<__float128, 28>(); // { dg-error "from here" }
>-  test_non_pow2<__float128, 29>(); // 4 calls
>-  test_pow2<__float128, 26>();
>-  test_pow2<__float128, 28>();
>-
>-  // N == 6: B == 22
>-  test_non_pow2<__float128, 21>(); // 6 calls
>-  test_non_pow2<__float128, 22>(); // { dg-error "from here" }
>-  test_non_pow2<__float128, 23>(); // 5 calls
>-  test_pow2<__float128, 22>();
>-}
>-
>-// { dg-prune-output "no type named 'type' in 'struct std::__detail::_Select_uint_least_t" }
>-// { dg-prune-output "static assertion failed: sorry, would be too much trouble for a slow result" }
>-- 
>2.55.0
>
>