On Wed, Dec 17, 2025 at 11:12 AM Jakub Jelinek <[email protected]> wrote:

> On Wed, Dec 17, 2025 at 04:30:17AM -0500, Nathan Myers wrote:
> > > So
> > >    constexpr _RealT __Rk = _RealT(_UInt(1) << (__log2_Rk - 1)) * 2.0;
> > > can be done in that case even using unsigned long long type.
> >
> > Shifting uint64_t(1) left 64 places, as would occur for
> > __d == 64 and _RealT=long double (with its 64-bit mantissa)
> > is UB and ill-formed in constexpr context...
>
> But if __log2_Rk is at most 64, then the above can still be done in
> uint64_t.
>
> > Emulating 128-bit integer operations seems like heroics unless C++
> > is not expected ever to get __int128. The intention was to support
>
> I think __int128 support is very unlikely coming for 32-bit targets,
> both for C and C++.  First of all, each target would need to define
> ABI for it (what alignment it has, how it is passed, how it is returned).
> As shown with _BitInt, that takes years even after getting it standardized.
> _BitInt got into C23, and in GCC 14/15 we got support only for 3 targets
> (x86_64, i686, aarch64) and only in GCC 16 we are getting a few others
> (s390x, loongarch, maybe riscv, maybe arm 32-bit).
> I think getting
> https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p3666r2.html
> in would be easier (but see above, it will take a few further years before
> all target maintains do something).
>
> > 128-bit floating-point formats, with mantissa >= 106 bits, in places
> > where users have them. Does it seem likely a quad-precision floating
> > point type would be implemented, but not __int128?
>
> That is a very common thing, std::float128_t is supported on many 32-bit
> targets.
>
Fortunately standard does not require generate_cannonical to work with
extended floating point types (including __float128_t), to be precise
the behavior is undefined per https://eel.is/c++draft/rand#req.genl-1.4.So
emitting static error in that case we are conforming.

Still we will need to check if we are causing any regressions here,
but the old implementation static_assert is_floating_point_v so we should
not
cause any, unless that allows __float128.

>
> For __generate_canonical_pow2 I think something like untested class
> below could do the job.
> __generate_canonical_any would be harder, that needs
> multiplication/division, and from what I can see, all this stuff is
> compiled
> with C++11 as well and so for constexpr we are limited by the C++11 return
> stmt only requirement.
>
> --- libstdc++-v3/include/bits/random.tcc.jj     2025-12-17
> 09:16:43.300578917 +0100
> +++ libstdc++-v3/include/bits/random.tcc        2025-12-17
> 10:58:32.541786465 +0100
> @@ -3481,6 +3481,64 @@ namespace __detail
>  #pragma GCC diagnostic ignored "-Wc++14-extensions" // for variable
> templates
>  #pragma GCC diagnostic ignored "-Wc++17-extensions" // if constexpr
>
> +#ifndef __SIZEOF_INT128__
> +  namespace __detail
> +  {
> +    struct __rng_uint128 {
> +      uint64_t _M_l, _M_h;
> +
> +      constexpr __rng_uint128(uint64_t __x) noexcept
> +      : _M_l (__x), _M_h (0) {}
> +
> +      constexpr __rng_uint128(uint64_t __h, uint64_t __l) noexcept
> +      : _M_l (__l), _M_h (__h) {}
> +
> +      template<typename _RealT>
> +      constexpr operator _RealT() noexcept {
> +       return _M_h == 0 ? _RealT(_M_l)
> +                        : _RealT(_M_h) * _RealT(18446744073709551616.0)
> +                          + _RealT(_M_l);
> +      }
> +    };
> +
> +    constexpr __rng_uint128
> +    operator+(const __rng_uint128& __x, const __rng_uint128& __y) noexcept
> +    {
> +      return __rng_uint128(__x._M_h + __y._M_h
> +                          + (__x._M_l + __y._M_l < __x._M_l),
> +                          __x._M_l + __y._M_l);
> +    }
> +
> +    constexpr __rng_uint128
> +    operator<<(const __rng_uint128& __x, size_t __y) noexcept
> +    {
> +      return (__y >= 64
> +             ? __rng_uint128(__x._M_l << (__y - 64), 0)
> +             : __y
> +             ? __rng_uint128((__x._M_h << __y) | (__x._M_l >> (64 - __y)),
> +                             __x._M_l << __y)
> +             : __x);
> +    }
> +
> +    constexpr __rng_uint128
> +    operator>>(const __rng_uint128& __x, size_t __y) noexcept
> +    {
> +      return (__y >= 64
> +             ? __rng_uint128(0, __x._M_h >> (__y - 64))
> +             : __y
> +             ? __rng_uint128(__x._M_h >> __y,
> +                             (__x._M_l >> __y) | (__x._M_h << (64 - __y)))
> +             : __x);
> +    }
> +
> +    constexpr __rng_uint128
> +    operator|(const __rng_uint128& __x, const __rng_uint128& __y) noexcept
> +    {
> +      return __rng_uint128(__x._M_h | __y._M_h, __x._M_l | __y._M_l);
> +    }
> +  }
> +#endif
> +
>    // This version is used when Urbg::max()-Urbg::min() is a power of
>    // two less 1, the norm in real programs. It works by calling urng()
>    // as many times as needed to fill the target mantissa, accumulating
> @@ -3511,7 +3569,8 @@ namespace __detail
>    // This implementation works with std::bfloat16, which can exactly
>    // represent 2^32, but not with std::float16_t, limited to 2^15.
>
> -  template<typename _RealT, typename _UInt, size_t __d, typename _Urbg>
> +  template<typename _RealT, typename _UInt, size_t __d, typename _Urbg,
> +          int __log2_uint_max>
>      _RealT
>      __generate_canonical_pow2(_Urbg& __urng)
>      {
> @@ -3521,10 +3580,8 @@ namespace __detail
>        // r = 2;  // Redundant, we only support radix 2.
>        using _Rng = decltype(_Urbg::max());
>        const _Rng __rng_range_less_1 = _Urbg::max() - _Urbg::min();
> -      const _UInt __uint_range_less_1 = ~_UInt(0);
>        // R = _UInt(__rng_range_less_1) + 1;  // May wrap to 0.
>        const auto __log2_R = __builtin_popcountg(__rng_range_less_1);
> -      const auto __log2_uint_max =
> __builtin_popcountg(__uint_range_less_1);
>        // rd = _UInt(1) << __d;  // Could overflow, UB.
>        const unsigned __k = (__d + __log2_R - 1) / __log2_R;
>        const unsigned __log2_Rk_max = __k * __log2_R;
> @@ -3532,7 +3589,8 @@ namespace __detail
>         __log2_uint_max < __log2_Rk_max ? __log2_uint_max : __log2_Rk_max;
>        static_assert(__log2_Rk >= __d);
>        // Rk = _UInt(1) << __log2_Rk;  // Likely overflows, UB.
> -      constexpr _RealT __Rk = _RealT(_UInt(1) << (__log2_Rk - 1)) * 2.0;
> +      constexpr _RealT __Rk
> +       = _RealT(_UInt(1) << (__log2_Rk - 1)) * _RealT(2.0);
>  #if defined(_GLIBCXX_GENERATE_CANONICAL_STRICT)
>        const unsigned __log2_x = __log2_Rk - __d; // # of spare entropy
> bits.
>  #else
> @@ -3593,7 +3651,7 @@ namespace __detail
>        const _UInt __rd = _UInt(1) << __d;
>        const unsigned __k = __gen_can_rng_calls_needed(__R, __rd);
>        const _UInt __Rk = __gen_can_pow(__R, __k);
> -      const _UInt __x =  __Rk/__rd;
> +      const _UInt __x =  __Rk / __rd;
>
>        while (true)
>         {
> @@ -3670,18 +3728,20 @@ namespace __detail
>        if constexpr (__is_power_of_2_less_1(_Urbg::max() - _Urbg::min()))
>         {
>           if constexpr (__d <= 32)
> -           return __generate_canonical_pow2<_RealT, uint32_t,
> __d>(__urng);
> +           return __generate_canonical_pow2<_RealT, uint32_t, __d,
> +                                            32>(__urng);
>           else if constexpr (__d <= 64)
> -           return __generate_canonical_pow2<_RealT, uint64_t,
> __d>(__urng);
> +           return __generate_canonical_pow2<_RealT, uint64_t, __d,
> +                                            64>(__urng);
>           else
>             {
> -#if defined(__SIZEOF_INT128__)
>               // Accommodate double double or float128.
> +#if defined(__SIZEOF_INT128__)
>               return __extension__ __generate_canonical_pow2<
> -               _RealT, unsigned __int128, __d>(__urng);
> +               _RealT, unsigned __int128, __d, 128>(__urng);
>  #else
> -             static_assert(false,
> -               "float precision >64 requires __int128 support");
> +             return __extension__ __generate_canonical_pow2<
> +               _RealT, __detail::__rng_uint128, __d, 128>(__urng);
>  #endif
>             }
>         }
>
>
>         Jakub
>
>

Reply via email to