Каков наилучший способ генерации случайного поплавка в С#?
Обновление: мне нужны случайные числа с плавающей запятой из float.Minvalue для float.Maxvalue. Я использую эти числа в модульном тестировании некоторых математических методов.
Каков наилучший способ генерации случайного поплавка в С#?
Обновление: мне нужны случайные числа с плавающей запятой из float.Minvalue для float.Maxvalue. Я использую эти числа в модульном тестировании некоторых математических методов.
Наилучший подход, без сумасшедших значений распределенный по представимым интервалам в числовой строке с плавающей запятой (удаляется "равномерным" по отношению к непрерывному числу строка явно неравномерна):
static float NextFloat(Random random)
{
double mantissa = (random.NextDouble() * 2.0) - 1.0;
double exponent = Math.Pow(2.0, random.Next(-126, 128));
return (float)(mantissa * exponent);
}
Другой подход, который даст вам некоторые безумные значения (равномерное распределение битовых шаблонов), потенциально полезное для fuzzing:
static float NextFloat(Random random)
{
var buffer = new byte[4];
random.NextBytes(buffer);
return BitConverter.ToSingle(buffer,0);
}
Наименее полезный подход:
static float NextFloat(Random random)
{
// Not a uniform distribution w.r.t. the binary floating-point number line
// which makes sense given that NextDouble is uniform from 0.0 to 1.0.
// Uniform w.r.t. a continuous number line.
//
// The range produced by this method is 6.8e38.
//
// Therefore if NextDouble produces values in the range of 0.0 to 0.1
// 10% of the time, we will only produce numbers less than 1e38 about
// 10% of the time, which does not make sense.
var result = (random.NextDouble()
* (Single.MaxValue - (double)Single.MinValue))
+ Single.MinValue;
return (float)result;
}
Число строк с плавающей точкой: Руководство разработчика программного обеспечения Intel Architecture Volume 1: Базовая архитектура. Ось Y является логарифмической (базовая 2), поскольку последовательные двоичные числа с плавающей запятой не отличаются линейно.
Любая причина не использовать Random.NextDouble
, а затем использовать для float
? Это даст вам поплавок между 0 и 1.
Если вам нужна другая форма "лучшего", вам нужно будет указать свои требования. Обратите внимание, что Random
не следует использовать для таких важных вопросов, как финансы или безопасность, - и вы должны, как правило, повторно использовать существующий экземпляр во всей своей программе или один для каждого потока (поскольку Random
не является потокобезопасным).
EDIT: как предложено в комментариях, чтобы преобразовать это в диапазон float.MinValue
, float.MaxValue
:
// Perform arithmetic in double type to avoid overflowing
double range = (double) float.MaxValue - (double) float.MinValue;
double sample = rng.NextDouble();
double scaled = (sample * range) + float.MinValue;
float f = (float) scaled;
EDIT: теперь вы упомянули, что это для модульного тестирования, я не уверен, что это идеальный подход. Вероятно, вам следует проверить конкретные значения - убедитесь, что вы тестируете образцы в каждой из соответствующих категорий - бесконечности, NaNs, денормальные числа, очень большие числа, ноль и т.д.
Еще одна версия... (я думаю, что это очень хорошо)
static float NextFloat(Random random)
{
(float)(float.MaxValue * 2.0 * (rand.NextDouble()-0.5));
}
//inline version
float myVal = (float)(float.MaxValue * 2.0 * (rand.NextDouble()-0.5));
Я думаю, что это...
И еще одна версия... (не так хорошо, но публикуется в любом случае)
static float NextFloat(Random random)
{
return float.MaxValue * ((rand.Next() / 1073741824.0f) - 1.0f);
}
//inline version
float myVal = (float.MaxValue * ((rand.Next() / 1073741824.0f) - 1.0f));
Я думаю, что это...
Тестирование большинства функций на этой странице: (i7, выпуск, без отладки, 2 ^ 28 циклов)
Sunsetquest1: min: 3.402823E+38 max: -3.402823E+38 time: 3096ms
SimonMourier: min: 3.402823E+38 max: -3.402819E+38 time: 14473ms
AnthonyPegram:min: 3.402823E+38 max: -3.402823E+38 time: 3191ms
JonSkeet: min: 3.402823E+38 max: -3.402823E+38 time: 3186ms
Sixlettervar: min: 1.701405E+38 max: -1.701410E+38 time: 19653ms
Sunsetquest2: min: 3.402823E+38 max: -3.402823E+38 time: 2930ms
Я принял несколько иной подход, чем другие
static float NextFloat(Random random)
{
double val = random.NextDouble(); // range 0.0 to 1.0
val -= 0.5; // expected range now -0.5 to +0.5
val *= 2; // expected range now -1.0 to +1.0
return float.MaxValue * (float)val;
}
Комментарии объясняют, что я делаю. Получите следующий double, преобразуйте это число в значение от -1 до 1, а затем умножьте его на float.MaxValue
.
Другим решением является следующее:
static float NextFloat(Random random)
{
float f;
do
{
byte[] bytes = new byte[4];
random.NextBytes(bytes);
f = BitConverter.ToSingle(bytes, 0);
}
while (float.IsInfinity(f) || float.IsNaN(f));
return f;
}
Вот еще один способ, которым я придумал: Скажем, вы хотите получить поплавок между 5.5 и 7, с 3 десятичными знаками.
float myFloat;
int myInt;
System.Random rnd = new System.Random();
void GenerateFloat()
{
myInt = rnd.Next(1, 2000);
myFloat = (myInt / 1000) + 5.5f;
}
Таким образом, вы всегда получите большее число, чем 5.5, и меньшее число, чем 7.