uniform_real_distribution & л; флоат & GT; генерация всех возможных значений

В настоящее время я работаю над важной выборкой, и для целей тестирования мне нужно иметь возможность генерировать все возможные значения, которые uniform_real_distribution<float> может генерировать для интервала [0,1] (да, он тоже закрыт справа). Моя идея состояла в том, чтобы генерировать целые числа, которые я мог бы затем преобразовать в числа с плавающей точкой Из тестов, которые я сделал, кажется, что существует идеальная биекция между равномерными числами с одинарной точностью в [0,1] и целыми числами в [0,2 ^ 24] (меня немного беспокоит тот факт, что это не [0 , 2 ^ 24-1], и я все еще пытаюсь выяснить, почему, по-моему, 0 просто является особенным для чисел с плавающей запятой, а от 1 до 2 ^ 24 — с плавающей запятой, имеющей один и тот же показатель степени). Мой вопрос заключается в том, являются ли поплавки, сгенерированные таким образом, именно поплавками, которые могут быть сгенерированы из uniform_real_distribution<float>, Вы можете найти мое целое число <-> Поплавковые тесты ниже:

void floatIntegerBitsBijectionTest()
{
uint32 two24 = 1 << 24;
bool bij24Bits = true;
float delta = float(1.0) / float(two24);
float prev = float(0) / float(two24);
for (uint32 i = 1; i <= two24; ++i)
{
float uintMap = float(i) / float(two24);
if (uintMap - prev != delta || uint32(uintMap*float(two24)) != i)
{
std::cout << "No bijection exists between uniform floats in [0,1] and integers in [0,2^24].\n";
bij24Bits = false;
break;
}
prev = uintMap;
}
if(bij24Bits) std::cout << "A bijection exists between uniform floats in [0,1] and integers in [0,2^24].\n";
std::cout << "\n";

uint32 two25 = 1 << 25;
bool bij25Bits = true;
delta = float(1.0) / float(two25);
prev = float(0) / float(two25);
for (uint32 i = 1; i <= two25; ++i)
{
float uintMap = float(i) / float(two25);
if (uintMap - prev != delta || uint32(uintMap*float(two25)) != i)
{
std::cout << "No bijection exists between uniform floats in [0,1] and integers in [0,2^25].\n";
if (i == ((1 << 24) + 1)) std::cout << "The first non-uniformly distributed float corresponds to the integer 2^24+1.\n";

bij25Bits = false;
break;
}
prev = uintMap;
}
if (bij25Bits) std::cout << "A bijection exists between uniform floats in [0,1] and integers in [0,2^25].\n";
std::cout << "\n";bool bij25BitsS = true;
delta = 1.0f / float(two24);
prev = float(-two24) / float(two24);
for (int i = -two24+1; i <= two24; ++i)
{
float uintMap = float(i) / float(two24);
if (uintMap - prev != delta || int(uintMap*float(two24)) != i)
{
std::cout << i << " " << uintMap - prev << " " << delta << "\n";
std::cout << "No bijection exists between uniform floats in [-1,1] and integers in [-2^24,2^24].\n";
bij25BitsS = false;
break;
}
prev = uintMap;
}
if (bij25BitsS) std::cout << "A bijection exists between uniform floats in [-1,1] and integers in [-2^24,2^24].\n";
}

РЕДАКТИРОВАТЬ:

Несколько актуально:

https://crypto.stackexchange.com/questions/31657/uniformly-distributed-secure-floating-point-numbers-in-0-1

http://xoroshiro.di.unimi.it/random_real.c

https://www.reddit.com/r/programming/comments/29ducz/obtaining_uniform_random_floats_is_trickier_than/

https://lemire.me/blog/2017/02/28/how-many-floating-point-numbers-are-in-the-interval-01/

РЕДАКТИРОВАТЬ 2:

Мне наконец удалось выяснить, что uniform_real_distribution<float> по крайней мере, когда используется с mt19937 Движок при использовании его с шаблонными аргументами по умолчанию (я говорю о реализации, которая идет с VS2017). К сожалению, он просто генерирует случайное целое число в [0,2 ^ 32-1], преобразует его в число с плавающей точкой и затем делит его на 2 ^ 32. Излишне говорить, что это приводит к неравномерно распределенным числам с плавающей запятой. Я предполагаю, однако, что это работает для большинства практических целей, если не работать близко к точности различий между сгенерированными числами.

0

Решение

Я предполагаю, что реализация C ++ использует 32-битный базовый двоичный формат IEEE-754 для float, В этом формате представимые значения с плавающей точкой в ​​[1, 2] регулярно располагаются на расстоянии 2-23.

определять x с:

std::uniform_real_distribution<float> x(1, 2);

Тогда, предполагая uniform_real_distribution хорошо реализован и используется правильный двигатель, x(engine) - 1 будет генерировать значения, равные N / 223 для целых чисел N в [0, 223), с равномерным распределением.

У меня есть опасения по поводу спецификации uniform_real_distribution в C ++. Это определяется с точки зрения реальной арифметики. Требование, чтобы оно возвращало значения с постоянной плотностью вероятности, требует непрерывного набора чисел, который не обеспечивает формат с плавающей запятой. Кроме того, я не уверен, как реализации будут обрабатывать конечные точки.

Поскольку распределение было вынуждено быть дискретным, можно также использовать uniform_int_distribution и умножить образцы на 2-23 (доступно как numeric_limits<float>::epsilon()). Преимущество заключается в уточнении конечных точек и в удобной поддержке интервалов [0, 1) или [0, 1], по желанию.

Даже если стандарт C ++ не использует IEEE-754, представимые значения в [1, 2] должны быть равномерно распределены из-за описания в стандарте C ++ значений с плавающей запятой, представленных некоторым количеством цифр в определенной базе, умножается на базу, поднятую до некоторой степени Для нулевой степени значения от 1 до 2 будут разнесены в соответствии со значением самой младшей цифры в формате. Как и выше, это расстояние будет numeric_limits<float>::epsilon(),

1 Стандарт C ++ использует унаследованный термин «мантисса», но предпочтительный термин — «значимость».

2

Другие решения

Вы могли бы заставить проблему. Бросайте свой собственный генератор случайных чисел.

РЕДАКТИРОВАТЬ: я только что обнаружил std::generate_canonical<float>(), который делает то же самое, но не зависит от магического числа 24. Это работает, что из std::numerical_limits<float>::digits, так далее…

#include <random>

static const unsigned long big = 1 << 24;
static std::default_random_engine re;
static std::uniform_int_distribution<unsigned long> uint(0, big - 1);

float rand_float() {
return uint(re) / static_cast<float>(big);
}
1

По вопросам рекламы [email protected]