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
---
 libstdc++-v3/include/bits/random.tcc | 50 ++++++++++++++++++++++++++++
 1 file changed, 50 insertions(+)

diff --git a/libstdc++-v3/include/bits/random.tcc 
b/libstdc++-v3/include/bits/random.tcc
index 53ccacb2e38..941495654c0 100644
--- a/libstdc++-v3/include/bits/random.tcc
+++ b/libstdc++-v3/include/bits/random.tcc
@@ -3349,6 +3349,7 @@ namespace __detail
        }
     }
 
+#if __cplusplus < 202300
   template<typename _RealType, size_t __bits,
           typename _UniformRandomNumberGenerator>
     _RealType
@@ -3385,6 +3386,55 @@ namespace __detail
        }
       return __ret;
     }
+#else
+  template<typename _RealT, size_t __digits, typename _URBG>
+    _RealT
+    generate_canonical(_URBG& __urng)
+    {
+      static_assert(std::is_floating_point<_RealT>::value,
+       "template argument must be a floating point type");
+      static_assert(
+       requires { [](uniform_random_bit_generator auto) {}(__urng); },
+       "template argument must be an std::uniform_random_bit_generator");
+
+      const size_t __r = std::numeric_limits<_RealT>::radix;
+      const auto __rdiff = [](auto __u) constexpr -> _RealT
+       { return _RealT(__u - _URBG::min()); };
+      constexpr static _RealT __R = __rdiff(_URBG::max()) + _RealT(1.0);
+      const size_t __reg_digits = std::numeric_limits<_RealT>::digits;
+      const size_t __d = __reg_digits < __digits ? __reg_digits : __digits;
+      const auto __pow = [](_RealT __r, auto __d) consteval -> _RealT
+       {
+         _RealT __res = __r;
+         while (--__d) __res *= __r;
+         return __res;
+       };
+      constexpr static _RealT __rd = __pow(_RealT(__r), __d);
+      const int __k = [](_RealT __rd, _RealT __R) consteval
+       {
+         unsigned __k = 1;
+         _RealT __Ri = __R;
+         while (__Ri < __rd)
+           { __Ri *= __R, ++__k; }
+         return __k;
+       } (__rd, __R);
+      constexpr static _RealT __Rk = __pow(__R, __k);
+      constexpr static  _RealT __x = [](_RealT __Rk, _RealT __rd) consteval
+       { return std::floor(__Rk/__rd); }(__Rk, __rd);
+      constexpr static _RealT __xrd = __x * __rd;
+
+      _RealT __sum;
+      do
+       {
+         _RealT __Ri = _RealT(1.0);
+         __sum = __rdiff(__urng());
+         for (int __i = 1; __i != __k; ++__i)
+           __Ri *= __R, __sum += __rdiff(__urng()) * __Ri;
+       } while (__builtin_expect(__sum >= __xrd, false));
+      _RealT __ret = std::floor(__sum / __x) / __rd;
+      return __ret;
+    }
+#endif
 
 _GLIBCXX_END_NAMESPACE_VERSION
 } // namespace
-- 
2.50.0

Reply via email to