On Sat, Aug 9, 2025 at 2:00 PM Nathan Myers <n...@cantrip.org> wrote:
> 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); > You do not need static here I believe, constexpr should be sufficient. Same for all other variables. > + 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 > I would use __builtin_pow here, it is constexpr in more modes than corresponding standard function, similarly for floor. > + { > + _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 > I will use if constexpr for __R being a power of two, and use __builtin_log in that case. You may need to add #pragma GCC diagnostic ignored "-Wc++17-extensions" sections around it to avoid issues. > + { > + 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); > I think this will not compile in clang, even built-ins are not constexpr there. > + 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 > >