56 votes

Meilleure façon de générer un flottant aléatoire en C #

Quelle est la meilleure façon de générer un flottant aléatoire en C #?

Mise à jour: je veux des nombres à virgule flottante aléatoires de float.Minvalue à float.Maxvalue. J'utilise ces chiffres dans les tests unitaires de certaines méthodes mathématiques.

70voto

user7116 Points 39829

La meilleure approche, pas folles les valeurs, distribué à l'égard de la représentable intervalles sur le nombre à virgule flottante ligne (supprimé "uniforme" comme à l'égard d'un nombre continu en ligne, il est résolument non-uniforme):

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);
}

Une autre approche qui va vous donner un peu folle de valeurs (distribution uniforme de modèles de bits), potentiellement utile pour le fuzzing:

static float NextFloat(Random random)
{
    var buffer = new byte[4];
    random.NextBytes(buffer);
    return BitConverter.ToSingle(buffer,0);
}

Le moins utile à l'approche:

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;
}

Nombre à virgule flottante ligne à partir de: Architecture Intel Software developer's Manual Volume 1: Architecture de Base. L'axe des ordonnées est logarithmique (base 2), car consécutives binaire des nombres à virgule flottante ne diffèrent pas de façon linéaire.

Comparison of distributions, logarithmic Y-axis

26voto

Jon Skeet Points 692016

Aucune raison de ne pas utiliser Random.NextDouble , puis jeté à l' float? Qui vous donnera un flottant entre 0 et 1.

Si vous souhaitez une autre forme de "meilleur", vous aurez besoin de spécifier vos besoins. Notez que Random ne doit pas être utilisé pour des sujets sensibles tels que la finance ou de la sécurité - et vous devriez généralement la réutilisation d'une instance existante tout au long de votre application, ou de l'une par thread (comme Random n'est pas thread-safe).

EDIT: Comme suggéré dans les commentaires, afin de le convertir à une gamme d' 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: Maintenant, vous avez mentionné que c'est pour les tests unitaires, je ne suis pas sûr que c'est une approche idéale. Vous devriez probablement tester avec une valeur concrète au lieu de décisions, assurez-vous de test avec des échantillons dans chacune des catégories pertinentes - infinities, NaNs, des nombres dénormalisés des chiffres, des nombres très grands, zéro, etc.

3voto

Anthony Pegram Points 58528

J'ai adopté une approche légèrement différente des autres

 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;
}
 

Les commentaires expliquent ce que je fais. Obtenez le double suivant, convertissez ce nombre en une valeur comprise entre -1 et 1, puis multipliez cela par float.MaxValue .

0voto

Simon Mourier Points 49585

Une autre solution consiste à faire ceci:

 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;
}
 

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X