#pragma GCC diagnostic ignored "-Wc++14-extensions" // for variable templates
#pragma GCC diagnostic ignored "-Wc++17-extensions" // if constexpr
- // 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
- // entropy into an integer value, converting that to the float type,
- // and then dividing by the range of the integer value (a constexpr
- // power of 2, so only adjusts the exponent) to produce a result in
- // [0..1]. In case of an exact 1.0 result, we re-try.
+ // __generate_canonical_pow2 is used when Urbg::max()-Urbg::min() is
+ // a power of two less 1. It works by calling urng() as many times as
+ // needed to fill the target mantissa, accumulating entropy into an
+ // integer value, converting that to the float type, and then dividing
+ // by the range of the integer value (a constexpr power of 2,
+ // so only adjusts the exponent) to produce a result in [0..1].
+ // In case of an exact 1.0 result, we re-try.
//
// It needs to work even when the integer type used is only as big
// as the float mantissa, such as uint64_t for long double. So,
// 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, size_t __d, typename _Urbg>
_RealT
__generate_canonical_pow2(_Urbg& __urng)
{
+ using _UInt = typename __detail::_Select_uint_least_t<__d>::type;
+
// Parameter __d is the actual target number of bits.
// Commented-out assignments below are of values specified in
// the Standard, but not used here for reasons noted.
// 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 = sizeof(_UInt) * __CHAR_BIT__;
// rd = _UInt(1) << __d; // Could overflow, UB.
const unsigned __k = (__d + __log2_R - 1) / __log2_R;
const unsigned __log2_Rk_max = __k * __log2_R;
__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
// (the range of values produced by the rng) up to twice the length
// of the mantissa.
- template<typename _RealT, typename _UInt, size_t __d, typename _Urbg>
+ template<typename _RealT, size_t __d, typename _Urbg>
_RealT
__generate_canonical_any(_Urbg& __urng)
{
- static_assert(__d < __builtin_popcountg(~_UInt(0)));
+ using _UInt = typename __detail::_Select_uint_least_t<__d * 2>::type;
+
// 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;
}
"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;
// If the RNG range is a power of 2 less 1, the float type mantissa
// 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
- }
- }
+ return __generate_canonical_pow2<_RealT, __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
- }
- }
+ return __generate_canonical_any<_RealT, __d>(__urng);
}
_GLIBCXX_END_INLINE_ABI_NAMESPACE(_V2)