Changes in v4:
 * Static-assert arg is floating-point, coercible from bigger unsigned.
 * Static-assert arg satisfies uniform_random_bit_generator, movable.
 * Include uniform_int_dist.h for concept uniform_random_bit_generator
 * Coerce floating consts from unsigned literals, matching other usage.
 * Remove redundant type decorations static and -> .
 * Use __builtin_floor for broader applicability than std::floor.
 * Use __builtin_pow in place of immediate lambda.
 * Rename, rearrange local objects for clarity.
 * Add redundant "if constexpr (k > 1)" for clarity.
 * #if-guard new impl against Clang, which lacks constexpr floor() etc.

Changes in v3:
 * Implement exactly as specified in the WP, for C++26 and up.
 * Eliminate massive overhead seen in present implementation.

Implement P0952R2 "A new specification for std::generate_canonical".
It has us start over if the naive generation of a float in 0..1
comes up exactly equal to 1, which occurs too rarely for the change
to measurably affect performance.

A test for the case already appears in 64351.cc. This change amounts
to a different resolution for PR64351 and LWG 2524.

libstdc++-v3/ChangeLog:
        PR libstdc++/119739
        * include/bits/random.tcc: Add generate_canonical impl for C++26.
        * include/std/random: Include uniform_int_dist.h for concept.
        * 
testsuite/26_numerics/random/uniform_real_distribution/operators/64351.cc:
        Adapt test for both pre- and post- C++26.
---
 libstdc++-v3/include/bits/random.tcc          | 47 +++++++++++++++++++
 libstdc++-v3/include/std/random               |  1 +
 .../operators/64351.cc                        |  8 +++-
 3 files changed, 54 insertions(+), 2 deletions(-)

diff --git a/libstdc++-v3/include/bits/random.tcc 
b/libstdc++-v3/include/bits/random.tcc
index 53ccacb2e38..7babb5c7826 100644
--- a/libstdc++-v3/include/bits/random.tcc
+++ b/libstdc++-v3/include/bits/random.tcc
@@ -3349,6 +3349,9 @@ namespace __detail
        }
     }
 
+#if __cplusplus < 202400 || defined(__clang__)
+  // Clang lacks constexpr __builtin_floor and __builtin_pow.
+
   template<typename _RealType, size_t __bits,
           typename _UniformRandomNumberGenerator>
     _RealType
@@ -3385,6 +3388,50 @@ namespace __detail
        }
       return __ret;
     }
+#else
+  template<typename _RealT, size_t __digits, typename _URBG>
+    _RealT
+    generate_canonical(_URBG& __urng)
+    {
+      static_assert(requires {
+       [](uniform_random_bit_generator auto&&) {}(std::move(__urng)); },
+       "argument must meet uniform_random_bit_generator requirements");
+      static_assert(
+       std::is_floating_point_v<_RealT> && requires { _RealT(_URBG::max()); },
+       "template argument must be floating point, coercible from unsigned");
+
+      // Names are chosen to match the description in the Standard.
+      const size_t __r = std::numeric_limits<_RealT>::radix;
+      const auto __sample = [](auto __high) constexpr
+       { return _RealT(__high - _URBG::min()); };
+      constexpr _RealT __R = _RealT(1) + __sample(_URBG::max());
+      const size_t __d_max = std::numeric_limits<_RealT>::digits;
+      const size_t __d = __digits < __d_max ? __digits : __d_max;
+      constexpr _RealT __rd = __builtin_pow(_RealT(__r), __d);
+      const unsigned __k = [](_RealT __R, _RealT __rd) consteval
+       {
+         unsigned __i = 1;
+         for (auto __Ri = __R; __Ri < __rd; __Ri *= __R)
+           ++__i;
+         return __i;
+       } (__R, __rd);
+      constexpr _RealT __Rk = __builtin_pow(__R, __k);
+      constexpr _RealT __x = __builtin_floor(__Rk / __rd);
+      constexpr _RealT __xrd = __x * __rd;
+
+      _RealT __sum;
+      do
+       {
+         _RealT __Ri = _RealT(1);
+         __sum = __sample(__urng());
+         if constexpr (__k > 1)
+           for (int __i = 1; __i != __k; ++__i)
+             __Ri *= __R, __sum += __sample(__urng()) * __Ri;
+       } while (__builtin_expect(__sum >= __xrd, false));
+      _RealT __ret = __builtin_floor(__sum / __x) / __rd;
+      return __ret;
+    }
+#endif
 
 _GLIBCXX_END_NAMESPACE_VERSION
 } // namespace
diff --git a/libstdc++-v3/include/std/random b/libstdc++-v3/include/std/random
index 0e058a04bd9..86d4f0a8380 100644
--- a/libstdc++-v3/include/std/random
+++ b/libstdc++-v3/include/std/random
@@ -49,6 +49,7 @@
 #include <type_traits>
 #include <bits/random.h>
 #include <bits/opt_random.h>
+#include <bits/uniform_int_dist.h>
 #include <bits/random.tcc>
 
 #endif // C++11
diff --git 
a/libstdc++-v3/testsuite/26_numerics/random/uniform_real_distribution/operators/64351.cc
 
b/libstdc++-v3/testsuite/26_numerics/random/uniform_real_distribution/operators/64351.cc
index 3c0cb8bbd49..6212a32a364 100644
--- 
a/libstdc++-v3/testsuite/26_numerics/random/uniform_real_distribution/operators/64351.cc
+++ 
b/libstdc++-v3/testsuite/26_numerics/random/uniform_real_distribution/operators/64351.cc
@@ -45,16 +45,20 @@ test02()
   rng.seed(sequence);
   rng.discard(12 * 629143);
   std::mt19937 rng2{rng};
+  int mismatch = 0;
   for (int i = 0; i < 20; ++i)
   {
     float n =
       std::generate_canonical<float, std::numeric_limits<float>::digits>(rng);
     VERIFY( n != 1.f );
 
-    // PR libstdc++/80137
     rng2.discard(1);
-    VERIFY( rng == rng2 );
   }
+  // PR libstdc++/80137
+#if __cplusplus >= 202400
+  rng2.discard(1);  // account for a 1.0 generated and discarded.
+#endif
+  VERIFY( rng == rng2 );
 }
 
 int
-- 
2.50.0

Reply via email to