4 ms·
weirdly enough, the provided implementation of rand_float() actually generates numbers in the interval [0, 1). I haven't read the implementation of _generate_c
by listeria 1y ago
weirdly enough, the provided implementation of rand_float() actually generates numbers in the interval [0, 1).
I haven't read the implementation of _generate_canonical_generic(), but the following special case in generate_canonical() applies here:
if constexpr (prng_is_bit_uniform && sizeof(T) == 4 && sizeof(generated_type) == 8) {
return (static_cast<std::uint32_t>(gen()) >> exponent_bits_32) * mantissa_hex_32;
}
which boils down to:
return (static_cast<std::uint32_t>(gen()) >> 8) * 0x1.p-24;
that is, a number in the interval [0, 2^24) divided by 2^24.
- GeorgeHaldane 1y agoThank you for noticing! Turns out [0, 1] is an artifact of the old documentation carried back from the time where generic GCC / clang approach used to produce occasional 1's due to some issues in N4958 specification. This is fixed in a new commit. For floats there are essentially 3 approaches that are selected based on the provided range & PRNG: 1) Shift + multiply (like `(rng >> 11) * 0x1.0p-53`) 2) "Low-high" from the paper by J. Doornik https://www.doornik.com/research/randomdouble.pdf 3) Canonical GCC implementation All of these generate values in [0, 1) range which is now reflected in the docs properly. For most actual cases 1st method is the one selected.
- listeria 1y agoIn this case I would suggest using the high bits of the RNG output when generating a float, since some generators have better entropy around the high bits. So when you're generating floats with a 64-bit generator, instead of masking out the high bits with the static_cast, you may want to use the following: return (gen() >> 40) * 0x1.0p-24;