On Wed, 17 Dec 2025 at 14:49, Jakub Jelinek <[email protected]> wrote:
>
> Hi!
>
> Actually, the
> 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
> errors are gone when testing with --target_board=unix/-m32/-msse2/-mfpmath=sse
> or when the tests are compiled with -O0, which means that it isn't buggy
> unsigned __int128 emulation in that case, but rather either those tests or
> something in random.tcc not being extended precision clean.
>
> So, here is the full patch.  It uses what Tomasz was asking for, i.e. for
> #ifdef __SIZEOF_INT128__ uses __extension__ using __rng_uint128 = unsigned 
> __int128;
> and otherwise the new class.  Tested on x86_64-linux and i686-linux, ok for
> trunk?

We already have a type that does part of this, see
_Select_uint_least_t::type at the top of <bits/random.h>

I've just finished a patch to extend that with the missing operations
needed to support std::philox_engine on 32-bit targets, so my work on
that will also be usable here. I will review what you've done below to
see if I can improve the code I have. But I'll push that work first
for philox, then we can extend it and use it for generate_canonical.



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

Our existing code uses the overflow checking built-in for addition:

      friend constexpr type
      operator+(type __l, uint64_t __c) noexcept
      {
        __l._M_hi += __builtin_add_overflow(__l._M_lo, __c, &__l._M_lo);
        return __l;
      }

I would expect that to be equivalent, right?

      friend constexpr type
      operator-(type __l, uint64_t __c) noexcept
      {
        __l._M_hi -= __builtin_sub_overflow(__l._M_lo, __c, &__l._M_lo);
        return __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);
> +    }

This is what I have (the [[likely]] attributes are because for philox
it usually shifts by exactly 64, but maybe for the general case we
shouldn't assume that):

      friend constexpr type
      operator>>(type __l, unsigned __m) noexcept
      {
        if (__m >= 64) [[__likely__]]
          {
            __l._M_lo = __l._M_hi >> (__m - 64);
            __l._M_hi = 0;
            return __l;
          }

        uint64_t __hi = __l._M_hi >> __m;
        uint64_t __lo = (__l._M_lo >> __m) | (__l._M_hi << (64 - __m));
        type __res(__lo);
        __res._M_hi = __hi;
        return __res;
      }

      friend constexpr type
      operator<<(type __l, unsigned __m) noexcept
      {
        if (__m >= 64) [[__likely__]]
          {
            __l._M_hi = __l._M_lo << (__m - 64);
            __l._M_lo = 0;
            return __l;
          }

        uint64_t __hi = (__l._M_hi << __m) | (__l._M_hi >> (64 - __m));
        uint64_t __lo = (__l._M_lo << __m);
        type __res(__lo);
        __res._M_hi = __hi;
        return __res;
      }

Reply via email to