The problem is in mapping from the codomain of std::mt19937
(std::uint_fast32_t
) to float
; the algorithm described by the standard gives incorrect results (inconsistent with its description of the output of the algorithm) when loss of precision occurs if the current IEEE754 rounding mode is anything other than round-to-negative-infinity (note that the default is round-to-nearest).
The 7549723rd output of mt19937 with your seed is 4294967257 (0xffffffd9u
), which when rounded to 32-bit float gives 0x1p+32
, which is equal to the max value of mt19937, 4294967295 (0xffffffffu
) when that is also rounded to 32-bit float.
The standard could ensure correct behavior if it were to specify that when converting from the output of the URNG to the RealType
of generate_canonical
, rounding is to be performed towards negative infinity; this would give a correct result in this case. As QOI, it would be good for libstdc++ to make this change.
With this change, 1.0
will no longer be generated; instead the boundary values 0x1.fffffep-N
for 0 < N <= 8
will be generated more often (approximately 2^(8 - N - 32)
per N
, depending on the actual distribution of MT19937).
I would recommend to not use float
with std::generate_canonical
directly; rather generate the number in double
and then round towards negative infinity:
double rd = std::generate_canonical<double,
std::numeric_limits<float>::digits>(rng);
float rf = rd;
if (rf > rd) {
rf = std::nextafter(rf, -std::numeric_limits<float>::infinity());
}
This problem can also occur with std::uniform_real_distribution<float>
; the solution is the same, to specialize the distribution on double
and round the result towards negative infinity in float
.
According to the standard, 1.0
is not valid.
C++11 §26.5.7.2 Function template generate_canonical
Each function instantiated from the template described in this section 26.5.7.2 maps the result of one or more invocations of a supplied uniform random number generator
g
to one member of the specified RealType such that, if the values gi produced byg
are uniformly distributed, the instantiation’s results tj , 0 ≤ tj < 1, are distributed as uniformly as possible as specified below.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With