C#でランダムフロートを生成する最良の方法は何ですか?
更新:float.Minvalueからfloat.Maxvalueまでのランダムな浮動小数点数が必要です。私はこれらの数値をいくつかの数学的方法の単体テストで使用しています。
最良のアプローチ、狂った値なし、 浮動小数点数の行で表現可能な間隔に関して分散 (連続した数の行に関して明らかに均一ではないため、「均一」を削除):
static float NextFloat(Random random)
{
double mantissa = (random.NextDouble() * 2.0) - 1.0;
// choose -149 instead of -126 to also generate subnormal floats (*)
double exponent = Math.Pow(2.0, random.Next(-126, 128));
return (float)(mantissa * exponent);
}
(*)... check here subnormal floatの場合
警告:正の無限大も生成します!安全のために、127の指数を選択します。
ファジングに潜在的に役立ついくつかのクレイジーな値(ビットパターンの均一な分布)を提供する別のアプローチ:
static float NextFloat(Random random)
{
var buffer = new byte[4];
random.NextBytes(buffer);
return BitConverter.ToSingle(buffer,0);
}
以前のバージョンと比べて改善されているのは、「狂った」値(無限大でもNaNでもない)を作成せず、依然として高速です(浮動小数点数行の表現可能な間隔に関しても分散されます)。
public static float Generate(Random prng)
{
var sign = prng.Next(2);
var exponent = prng.Next((1 << 8) - 1); // do not generate 0xFF (infinities and NaN)
var mantissa = prng.Next(1 << 23);
var bits = (sign << 31) + (exponent << 23) + mantissa;
return IntBitsToFloat(bits);
}
private static float IntBitsToFloat(int bits)
{
unsafe
{
return *(float*) &bits;
}
}
最も有用でないアプローチ:
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 Software Developer's Manual Volume 1:Basic Architecture。 Y軸は対数(2を底とする)です。これは、連続する2進浮動小数点数が線形的に異なるためです。
Random.NextDouble
を使用してからfloat
にキャストしない理由これにより、0〜1の浮動小数点数が得られます。
別の形式の「ベスト」が必要な場合は、要件を指定する必要があります。 Random
は、財務やセキュリティなどの機密事項には使用しないでください。通常、アプリケーション全体で既存のインスタンスを再利用するか、スレッドごとに1つ(Random
はスレッドではないため) -安全)。
編集:コメントで示唆されているように、これを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;
編集:これはユニットテスト用であると述べましたが、それが理想的なアプローチであるかどうかはわかりません。代わりに具体的な値でテストする必要があります-無限大、NaN、非正規数、非常に大きな数、ゼロなど、関連する各カテゴリのサンプルでテストしてください。
もう1つのバージョン...(これはかなり良いと思います)
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));
これだと思う...
そしてもう1つのバージョン...(あまり良くはありませんが、とにかく投稿します)
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
で乗算します。
ここに私が思いついた別の方法があります: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よりも小さい数が得られます。
次のコードを使用して、最初の小数点までの10進数を生成することをお勧めします。文字列「combined」にその番号を追加することにより、小数点の後にさらに番号を追加するために3行目をコピーして貼り付けることができます。 0と9を希望の値に変更することにより、最小値と最大値を設定できます。
Random r = new Random();
string beforePoint = r.Next(0, 9).ToString();//number before decimal point
string afterPoint = r.Next(0,9).ToString();//1st decimal point
//string secondDP = r.Next(0, 9).ToString();//2nd decimal point
string combined = beforePoint+"."+afterPoint;
decimalNumber= float.Parse(combined);
Console.WriteLine(decimalNumber);
それがあなたのお役に立てば幸いです。
別の解決策はこれを行うことです:
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;
}