[PATCH] libstdc++: Fix up all_pedantic_errors.cc [PR119739]
Tomasz Kaminski
tkaminsk@redhat.com
Wed Dec 17 10:44:31 GMT 2025
On Wed, Dec 17, 2025 at 11:12 AM Jakub Jelinek <jakub@redhat.com> 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
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://gcc.gnu.org/pipermail/libstdc++/attachments/20251217/dbd3e80f/attachment-0001.htm>
More information about the Libstdc++
mailing list