Да, этот вариант предпочтительней, но он появился только в C++11, поэтому в GF и использовали QueryPerformanceCounter(ведь счётчик тактов - корректный источник энтропии).инициализируя std::random_device
Как мне кажется, главная проблема - это всё же использование остатка от деления для установки диапазона.В своих фиксах для С1 я тоже переписал rand() на std::mt19937
И, как я уже показал выше, в GF её решили довольно элегантно. Вот для наглядности:
std::mt19937_64 генерирует целые числа в диапазоне [0, (2^64)-1] (0x0000000000000000, 0xFFFFFFFFFFFFFFFF).
Для получения double числа используются только 53 бита (0x001FFFFFFFFFFFFF) (количество неслучайно: связано с шириной мантиссы double).
Далее идёт умножение на один из постоянных double множителей:
DOUBLE_UNIT_EXCLUSIVE = 1.1102230246251565e-016 (0x3CA0000000000000)
или
DOUBLE_UNIT_INCLUSIVE = 1.1102230246251568e-016 (0x3CA0000000000001)
(оба они примерно равны 0.00000000000000011).
После преобразования ряды выглядят так:
C++:
(double)0x0000000000000000 * DOUBLE_UNIT_EXCLUSIVE = 0.00000000000000000
(double)0x0000000000000001 * DOUBLE_UNIT_EXCLUSIVE ~= 0.00000000000000011
(double)0x0000000000000002 * DOUBLE_UNIT_EXCLUSIVE ~= 0.00000000000000022
...
(double)0x001FFFFFFFFFFFFD * DOUBLE_UNIT_EXCLUSIVE ~= 0.99999999999999967
(double)0x001FFFFFFFFFFFFE * DOUBLE_UNIT_EXCLUSIVE ~= 0.99999999999999978
(double)0x001FFFFFFFFFFFFF * DOUBLE_UNIT_EXCLUSIVE ~= 0.99999999999999989
C++:
(double)0x0000000000000000 * DOUBLE_UNIT_INCLUSIVE = 0.00000000000000000
(double)0x0000000000000001 * DOUBLE_UNIT_INCLUSIVE ~= 0.00000000000000011
(double)0x0000000000000002 * DOUBLE_UNIT_INCLUSIVE ~= 0.00000000000000022
...
(double)0x001FFFFFFFFFFFFD * DOUBLE_UNIT_INCLUSIVE ~= 0.99999999999999989
(double)0x001FFFFFFFFFFFFE * DOUBLE_UNIT_INCLUSIVE ~= 1.00000000000000000
(double)0x001FFFFFFFFFFFFF * DOUBLE_UNIT_INCLUSIVE = 1.00000000000000000
В итоге получается 2^53 double значений в диапазоне от 0.0 до 1.0 (проще говоря от 0% до 100%) c шагом ~0.00000000000000011.