First recognizing that finding a random number [0...a]
is a sufficient step, followed by a coin flip for +/-
.
Step 2. Find the expo
such that a < 2**expo
or ceil(log2(a))
.
int sign;
do {
int exp;
frexp(a, &exp);
Step 3. Form an integral 63-bit random number [0...0x7FFF_FFFF_FFFF_FFFF]
and random sign. The 63 should be at least as wide as the precision of a double
- which is often 53 bits. At this point r
is certainly uniform.
unit64_t r = randombytes_uniform(0xFFFFFFFF);
r <<= 32;
r |= randombytes_uniform(0xFFFFFFFF);
// peel off one bit for sign
sign = r & 1;
r >>= 1;
Step 4. Scale and test if in range. Repeat as needed.
double candidate = ldexp(r/pow(2 63), expo);
} while (candidate > a);
Step 5. Apply the sign.
if (sign) {
candidate = -candidate;
}
return candidate;
Avoid (2.0 * x / a) - 1
as the calculation is not symmetric about 0.0.
Code would benefit with improvements to deal with a
near DBL_MAX
.
Some rounding issues apply that this answer glosses over, yet the distribution remains uniform - except potentially at the edges.