[PATCH 2/3] libstdc++: Fix up std::generate_canonical for 32-bit arches
Jonathan Wakely
jwakely.gcc@gmail.com
Wed Dec 17 20:36:25 GMT 2025
On Wed, 17 Dec 2025 at 20:32, Tomasz Kaminski <tkaminsk@redhat.com> wrote:
>
>
>
> On Wed, Dec 17, 2025 at 8:51 PM Jonathan Wakely <jwakely@redhat.com> wrote:
>>
>> Make use of __detail::_Select_uint_least_t<d>::type for
>> std::generate_canonical, so that we have a 128-bit integer type even on
>> 32-bit targets.
>>
>> libstdc++-v3/ChangeLog:
>>
>> * include/bits/random.tcc (__generate_canonical_pow2): Elide
>> variable into poopcount call. Cast floating-point literal to
>> _RealT.
>> (__generate_canonical_any): Use direct-initialization for _UInt
>> variables. Cast floating-point literal to _RealT.
>> (generate_canonical): Remove unused typedef. Use
>> _Select_uint_least_t to select the unsigned integer type to use.
>>
>> Co-authored-by: Jakub Jelinek <jakub@redhat.com>
>> ---
>>
>> Tested x86_64-linux
>> Also checked -m32 and -D_GLIBCXX_USE_OLD_GENERATE_CANONICAL
>>
>> libstdc++-v3/include/bits/random.tcc | 51 +++++++---------------------
>> 1 file changed, 12 insertions(+), 39 deletions(-)
>>
>> diff --git a/libstdc++-v3/include/bits/random.tcc b/libstdc++-v3/include/bits/random.tcc
>> index 68eae419b721..6d9ce7d6a623 100644
>> --- a/libstdc++-v3/include/bits/random.tcc
>> +++ b/libstdc++-v3/include/bits/random.tcc
>> @@ -3522,10 +3522,9 @@ 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);
>> + const auto __log2_uint_max = __builtin_popcountg(~_UInt(0));
>> // rd = _UInt(1) << __d; // Could overflow, UB.
>> const unsigned __k = (__d + __log2_R - 1) / __log2_R;
>> const unsigned __log2_Rk_max = __k * __log2_R;
>> @@ -3533,7 +3532,7 @@ 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
>> @@ -3586,25 +3585,25 @@ 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;
>> + const _UInt __r{2};
>> const _UInt __R = _UInt(_Urbg::max() - _Urbg::min()) + 1;
>> 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)
>> {
>> - _UInt __Ri = 1, __sum = __urng() - _Urbg::min();
>> + _UInt __Ri{1};
>> + _UInt __sum{__urng() - _Urbg::min()};
>> for (int __i = __k - 1; __i > 0; --__i)
>> {
>> __Ri *= __R;
>> - __sum += (__urng() - _Urbg::min()) * __Ri;
>> + __sum += _UInt{__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;
>> }
>> @@ -3660,7 +3659,6 @@ _GLIBCXX_BEGIN_INLINE_ABI_NAMESPACE(_V2)
>> "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;
>>
>> @@ -3668,38 +3666,13 @@ _GLIBCXX_BEGIN_INLINE_ABI_NAMESPACE(_V2)
>> // is enough bits. If not, we need more.
>> 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);
>> - else if constexpr (__d <= 64)
>> - return __generate_canonical_pow2<_RealT, uint64_t, __d>(__urng);
>> - else
>> - {
>> -#if defined(__SIZEOF_INT128__)
>> - // Accommodate double double or float128.
>> - return __extension__ __generate_canonical_pow2<
>> - _RealT, unsigned __int128, __d>(__urng);
>> -#else
>> - static_assert(false,
>> - "float precision >64 requires __int128 support");
>> -#endif
>> - }
>> + using type = typename __detail::_Select_uint_least_t<__d>::type;
>> + return __generate_canonical_pow2<_RealT, type, __d>(__urng);
>> }
>> else // Need up to 2x bits.
>> {
>> - if constexpr (__d <= 32)
>> - 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");
>> - return __extension__ __generate_canonical_any<
>> - _RealT, unsigned __int128, __d>(__urng);
>> -#else
>> - static_assert(false, "irregular RNG with float precision"
>> - " >32 requires __int128 support");
>> -#endif
>> - }
>> + using type = typename __detail::_Select_uint_least_t<__d * 2>::type;
>
> There are two problems here:
> a) for any system where long double has mantissa larger than 64 (i.e. uses some version of __float128)
> this will be ill-formed, as we do not provide integers longer than 128 (regardless of emulation)
Yeah, this only solves the problem for some targets (e.g. i686 and arm).
> b) for some combinations of the URGB and _FloatT, R^k would still fit in 32,64 bits even
> if __float128 is available.
> Doing b) help to partially address the problem a, as assuming URBG producing less <= 64bits
> on entropy at some time, we may be able to fit between long double digits, and 128.
> I will update my patch that I posted before to be based on that (I think I can implement it a lot nicer).
> But this is an improvement, so LGTM.
>
>>
>> + return __generate_canonical_any<_RealT, type, __d>(__urng);
>> }
>> }
>> _GLIBCXX_END_INLINE_ABI_NAMESPACE(_V2)
>> --
>> 2.52.0
>>
More information about the Libstdc++
mailing list