[PATCH] libstdc++: Fix up all_pedantic_errors.cc [PR119739]
Jakub Jelinek
jakub@redhat.com
Wed Dec 17 10:11:22 GMT 2025
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.
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
More information about the Libstdc++
mailing list