On Wed, Dec 17, 2025 at 1:58 PM Jakub Jelinek <[email protected]> wrote:

> Hi!
>
> Here is an updated patch.
> In
> make check -j32 -k RUNTESTFLAGS="--target_board=unix\{-m32,-m64\}
> conformance.exp='114359.cc 58302.cc 64351.cc 83833.cc 91371.cc
> call-default.cc equal.cc gencanon.cc generate.cc pr60037-neg.cc
> serialize.cc 93978.cc'"
> testing I still see
> FAIL: 26_numerics/random/pr60037-neg.cc  -std=gnu++20  (test for errors,
> line 3650)
> FAIL: 26_numerics/random/pr60037-neg.cc  -std=gnu++20 (test for excess
> errors)
> FAIL: 26_numerics/random/uniform_real_distribution/operators/64351.cc
> -std=gnu++20 execution test
> FAIL: 26_numerics/random/uniform_real_distribution/operators/gencanon.cc
> -std=gnu++20 execution test
> FAIL: 26_numerics/random/pr60037-neg.cc  -std=gnu++20  (test for errors,
> line 3650)
> FAIL: 26_numerics/random/pr60037-neg.cc  -std=gnu++20 (test for excess
> errors)
> I think Tomasz dealt with pr60037-neg.cc, and the 2 FAILs are -m32 only
> /usr/src/gcc/libstdc++-v3/testsuite/26_numerics/random/uniform_real_distribution/operators/64351.cc:42:
> void test02(): Assertion 'rng == rng2' failed.
> and
> /usr/src/gcc/libstdc++-v3/testsuite/26_numerics/random/uniform_real_distribution/operators/gencanon.cc:152:
> void test00(): Assertion 'deviation == 7032' failed.
> so guess I need to still debug that.
>
> --- libstdc++-v3/include/bits/random.tcc.jj     2025-12-17
> 11:22:03.823493462 +0100
> +++ libstdc++-v3/include/bits/random.tcc        2025-12-17
> 13:44:24.571984415 +0100
> @@ -3481,6 +3481,211 @@ 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
> +  {
> +    // Note, this is not a full unsigned __int128 emulation, just
> +    // enough for __generate_* purposes below.
> +    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(const __rng_uint128& __x) noexcept =
> default;
> +
> +      constexpr __rng_uint128(uint64_t __h, uint64_t __l) noexcept
> +      : _M_l (__l), _M_h (__h) {}
> +
> +      template<typename _RealT>
> +      constexpr operator _RealT() const noexcept {
> +       static_assert(std::is_floating_point<_RealT>::value,
> +         "template argument must be a floating point type");
> +       return _M_h == 0 ? _RealT(_M_l)
> +                        : _RealT(_M_h) * _RealT(18446744073709551616.0)
> +                          + _RealT(_M_l);
> +      }
> +
> +      __rng_uint128& operator*=(const __rng_uint128& __x) noexcept
> +      {
> +       uint64_t __a[12] = {
> +         uint32_t(_M_l), _M_l >> 32,
> +         uint32_t(_M_h), _M_h >> 32,
> +         uint32_t(__x._M_l), __x._M_l >> 32,
> +         uint32_t(__x._M_h), __x._M_h >> 32,
> +         0, 0,
> +         0, 0 };
> +       for (int __i = 0; __i < 4; ++__i)
> +         {
> +           uint64_t __c = 0;
> +           for (int __j = __i; __j < 4; ++__j)
> +             {
> +               __c += __a[__i] * __a[4 + __j - __i] + __a[8 + __j];
> +               __a[8 + __j] = uint32_t(__c);
> +               __c >>= 32;
> +             }
> +         }
> +       _M_l = __a[8] + (__a[9] << 32);
> +       _M_h = __a[10] + (__a[11] << 32);
> +       return *this;
> +      }
> +    };
> +
> +    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);
> +    }
> +
> +    __rng_uint128
> +    operator*(const __rng_uint128& __x, const __rng_uint128& __y) noexcept
> +    {
> +      __rng_uint128 __ret(__x);
> +      __ret *= __y;
> +      return __ret;
> +    }
> +
> +    __rng_uint128
> +    operator/(const __rng_uint128& __x, const __rng_uint128& __y) noexcept
> +    {
> +      uint64_t __l, __h;
> +      if (!__x._M_h)
> +       {
> +         if (!__y._M_h)
> +           __l = __x._M_l / __y._M_l;
> +         else
> +           __l = 0;
> +         __h = 0;
> +       }
> +      else
> +       {
> +         uint64_t __a[13] = {
> +           uint32_t(__x._M_l), __x._M_l >> 32,
> +           uint32_t(__x._M_h), __x._M_h >> 32,
> +           0,
> +           uint32_t(__y._M_l), __y._M_l >> 32,
> +           uint32_t(__y._M_h), __y._M_h >> 32,
> +           0, 0,
> +           0, 0 };
> +         uint64_t __c = 0, __w = 0;
> +         if (!__y._M_h && __y._M_l <= ~uint32_t(0))
> +           for (int __i = 3; ; --__i)
> +             {
> +               __w = __a[__i] + (__c << 32);
> +               __a[9 + __i] = __w / __y._M_l;
> +               if (__i == 0)
> +                 break;
> +               __c = __w % __y._M_l;
> +             }
> +         else
> +           {
> +             // See Donald E. Knuth's "Seminumerical Algorithms".
> +             int __n = 0, __d = 0;
> +             uint64_t __q = 0, __s = 0;
> +             for (__d = 3; __a[5 + __d] == 0; --__d)
> +               ;
> +             __s = (uint64_t(1) << 32) / (__a[5 + __d] + 1);
> +             if (__s > 1)
> +               {
> +                 for (int __i = 0; __i <= 3; ++__i)
> +                   {
> +                     __w = __a[__i] * __s + __c;
> +                     __a[__i] = uint32_t(__w);
> +                     __c = __w >> 32;
> +                   }
> +                 __a[4] = __c;
> +                 __c = 0;
> +                 for (int __i = 0; __i <= 3; ++__i)
> +                   {
> +                     __w = __a[5 + __i] * __s + __c;
> +                     __a[5 + __i] = uint32_t(__w);
> +                     __c = __w >> 32;
> +                     if (__a[5 + __i])
> +                       __d = __i;
> +                   }
> +               }
> +             __n = 4;
> +             for (int __i = __n - __d - 1; __i >= 0; --__i)
> +               {
> +                 __n = __i + __d + 1;
> +                 __w = (__a[__n] << 32) + __a[__n - 1];
> +                 if (__a[__n] != __a[5 + __d])
> +                   __q = __w / __a[5 + __d];
> +                 else
> +                   __q = ~uint32_t(0);
> +                 uint64_t __t = __w - __q * __a[5 + __d];
> +                 if (__t <= ~uint32_t(0)
> +                     && __a[4 + __d] * __q > (__t << 32) + __a[__n - 2])
> +                   --__q;
> +                 __c = 0;
> +                 for (int __j = 0; __j <= __d; ++__j)
> +                   {
> +                     __w = __q * __a[5 + __j] + __c;
> +                     __c = __w >> 32;
> +                     __w = __a[__i + __j] - uint32_t(__w);
> +                     __a[__i + __j] = uint32_t(__w);
> +                     __c += (__w >> 32) != 0;
> +                   }
> +                 if (int64_t(__a[__n]) < int64_t(__c))
> +                   {
> +                     --__q;
> +                     __c = 0;
> +                     for (int __j = 0; __j <= __d; ++__j)
> +                       {
> +                         __w = __a[__i + __j] + __a[5 + __j] + __c;
> +                         __c = __w >> 32;
> +                         __a[__i + __j] = uint32_t(__w);
> +                       }
> +                     __a[__n] += __c;
> +                   }
> +                 __a[9 + __i] = __q;
> +               }
> +           }
> +         __l = __a[9] + (__a[10] << 32);
> +         __h = __a[11] + (__a[12] << 32);
> +       }
> +      return __rng_uint128(__h, __l);
> +    }
> +
> +    constexpr bool
> +    operator<(const __rng_uint128 __x, const __rng_uint128 __y)
> +    {
> +      return (__x._M_h < __y._M_h
> +             || (__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 +3716,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, int
> __log2_uint_max,
> +          typename _Urbg>
>      _RealT
>      __generate_canonical_pow2(_Urbg& __urng)
>      {
> @@ -3521,10 +3727,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 +3736,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
> @@ -3585,7 +3790,6 @@ namespace __detail
>      _RealT
>      __generate_canonical_any(_Urbg& __urng)
>      {
> -      static_assert(__d < __builtin_popcountg(~_UInt(0)));
>        // Names below are chosen to match the description in the Standard.
>        // Parameter d is the actual target number of bits.
>        const _UInt __r = 2;
> @@ -3593,7 +3797,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)
>         {
> @@ -3601,9 +3805,9 @@ namespace __detail
>           for (int __i = __k - 1; __i > 0; --__i)
>             {
>               __Ri *= __R;
> -             __sum += (__urng() - _Urbg::min()) * __Ri;
> +             __sum = __sum + (__urng() - _Urbg::min()) * __Ri;
>             }
> -         const _RealT __ret = _RealT(__sum / __x) / __rd;
> +         const _RealT __ret = _RealT(__sum / __x) / _RealT(__rd);
>           if (__ret < _RealT(1.0))
>             return __ret;
>         }
> @@ -3658,7 +3862,6 @@ namespace __detail
>         "float16_t type is not supported, consider using bfloat16_t");
>  #endif
>
> -      using _Rng = decltype(_Urbg::max());
>        const unsigned __d_max = std::numeric_limits<_RealT>::digits;
>        const unsigned __d = __digits > __d_max ? __d_max : __digits;
>
> @@ -3667,18 +3870,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);
>
Do we have option to use typedef/using with __extentsion, define something
like in the function
#if defined(__SIZEOF_INT128__)
   using __uint128 =  usingned __uint128;
#else
     using __uint128 =  __detail::__rng_uint128;
#endif
Then we later could use __uint128 instead of repeated duplicated branches?


>  #endif
>             }
>         }
> @@ -3688,14 +3893,14 @@ namespace __detail
>             return __generate_canonical_any<_RealT, uint64_t, __d>(__urng);
>           else
>             {
> -#if defined(__SIZEOF_INT128__)
>               static_assert(__d <= 64,
>                 "irregular RNG with float precision >64 is not supported");
> +#if defined(__SIZEOF_INT128__)
>               return __extension__ __generate_canonical_any<
>                 _RealT, unsigned __int128, __d>(__urng);
>  #else
> -             static_assert(false, "irregular RNG with float precision"
> -                " >32 requires __int128 support");
> +             return __extension__ __generate_canonical_any<
> +               _RealT, __detail::__rng_uint128, __d>(__urng);
>  #endif
>             }
>         }
>
>
>         Jakub
>
>

Reply via email to