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);
#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