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
>
>