Сначала признается, что нахождение случайного числа [0...a]
является достаточным шагом, затем следует бросок монеты для +/-
.
Шаг 2. Найдите expo
, такой что a < 2**expo
или ceil(log2(a))
.
int sign;
do {
int exp;
frexp(a, &exp);
Шаг 3. Сформировать целое 63-битное случайное число [0...0x7FFF_FFFF_FFFF_FFFF]
и случайный знак.63 должен быть по крайней мере такой же ширины, как точность double
, что часто составляет 53 бита.На данный момент r
определенно равномерно .
unit64_t r = randombytes_uniform(0xFFFFFFFF);
r <<= 32;
r |= randombytes_uniform(0xFFFFFFFF);
// peel off one bit for sign
sign = r & 1;
r >>= 1;
Шаг 4. Масштабируйте и проверьте, находится ли в диапазоне.При необходимости повторите.
double candidate = ldexp(r/pow(2 63), expo);
} while (candidate > a);
Шаг 5. Примените знак.
if (sign) {
candidate = -candidate;
}
return candidate;
Избегайте (2.0 * x / a) - 1
, так как расчет не симметричен относительно 0,0.
Код выиграл бы с улучшениями, чтобы иметь дело с a
около DBL_MAX
.
Некоторые проблемы округления применяются, что этот ответ замаскирован, но распределение остается однородным - за исключением потенциально на краях.