Closed vigna closed 4 years ago
The relevant code is here: https://github.com/rust-random/rand/blob/master/src/distributions/float.rs#L102 (except cast_from_int
which is an as
cast (see distributions/utils.rs
) and into_float_with_exponent
defined above).
We implement three cases:
Standard
(i.e. [0, 1)
): take 53 bits of the u64
value, cast to a double, and divide by 1<<53
OpenClosed01
(i.e. (0, 1]
): same as above except that we add 1 to the 53-bit value before casting (this only sets the 54-th bit when all other bits are zero, so can still be exactly represented as a double)Open01
(i.e. (0, 1)
): use bit-casting to generate a double in [1, 2)
, then subtract 1 - ε/2
; this does lose 1 bit of precision[0, 1]
is never technically needed in practice and a uniform implementation must mostly "waste" one bit, we omit this variant.)Yes, none of these are perfect (since values smaller than 2^-53
are not generated and small values do not have full precision), but we considered these the best fit for typical usage (but also see https://github.com/rust-random/rand/pull/531).
We also had an implementation using exactly 32/64 random bits, but ended up not using it: https://github.com/rust-random/rand/pull/372
Yes, we (especially @pitdicker and @sicking) thought a lot about this. You think more of this should be in the book?
They're totally fine (maybe the methods returning just 52 bits should be marked). I do the same. I'm not commenting that, I'm commenting the book.
The phrase above in the book does not sound anything like whay you're describing. It sounds like the method I put a link to.
In the book, I read:
"f64: we treat this as an approximation of the real numbers, and, by convention, restrict to the range 0 to 1 (if not otherwise specified). Note that this type has finite precision, so we use the coin-flipping method above (but with random bits instead of coins) until we get as much precision as the type can represent; however, since floating-point numbers are much more precise close to 0 than they are near 1, we typically simplify here and stop once we have enough precision to differentiate between 1 and the next smallest value representable (1 - ε/2)."
This looks like you're generating float values using the high-precision methods described here: http://prng.di.unimi.it/random_real.c
But when I look at the code (sorry, I don't speak Rust, so this might be wrong) looks like you're using the multiplication-free method that gives 52 bits of precision instead of 53. Is it so? In this case, maybe this should be specified in the book.