Beste manier om een ​​willekeurige float te genereren in C#

Wat is de beste manier om een ​​willekeurige float in C# te genereren?

Update: ik wil willekeurige getallen met drijvende komma van float.Minvalue naar float.Maxvalue. Ik gebruik deze getallen bij het testen van eenheden van een aantal wiskundige methoden.


Antwoord 1, autoriteit 100%

Beste aanpak, geen gekke waarden, verdeeld met betrekking tot de representatieve intervallen op de getallenlijn met drijvende komma(verwijderd “uniform” omdat het met betrekking tot een doorlopende getallenlijn beslist niet-uniform is):

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

(*) … kijk hiervoor subnormale floats

Waarschuwing: genereert ook positieve oneindigheid! Kies voor de zekerheid een exponent van 127.

Een andere benadering die je een aantal gekke waarden geeft (uniforme verdeling van bitpatronen), mogelijk handig voor fuzzing:

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

Een verbetering ten opzichte van de vorige versie is deze, die geen “gekke” waarden creëert (noch oneindig noch NaN) en nog steeds snel is (ook verdeeld met betrekking tot de representeerbare intervallen op de getallenlijn met drijvende komma):

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

Minst bruikbare benadering:

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

Drijvende-kommagetalregel uit: Intel Architecture Software Developer’s Manual Volume 1: Basic Architecture.De Y-as is logaritmisch (grondtal-2) omdat opeenvolgende binaire getallen met drijvende komma niet lineair verschillen.

Vergelijking van distributies, logaritmische Y-as


Antwoord 2, autoriteit 38%

Is er een reden om Random.NextDoubleniet te gebruiken en vervolgens te casten naar float? Dat geeft je een float tussen 0 en 1.

Als u een andere vorm van ‘beste’ wilt, moet u uw vereisten specificeren. Houd er rekening mee dat Randomniet mag worden gebruikt voor gevoelige zaken zoals financiën of beveiliging – en dat je over het algemeen een bestaande instantie in je hele applicatie moet hergebruiken, of één per thread (als Randomis niet thread-safe).

EDIT: zoals voorgesteld in opmerkingen, om dit te converteren naar een bereik van 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: Nu je hebt gezegd dat dit voor unit-testing is, weet ik niet zeker of dit een ideale benadering is. U moet in plaats daarvan waarschijnlijk testen met concrete waarden – zorg ervoor dat u test met steekproeven in elk van de relevante categorieën – oneindigheden, NaN’s, denormale getallen, zeer grote getallen, nul, enz.


Antwoord 3, autoriteit 7%

Nog een versie… (Ik vind deze best goed)

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

Ik denk dat dit…

  • is de 2e snelste (zie benchmarks)
  • is gelijk verdeeld

En nog een versie…(niet zo goed maar toch geplaatst)

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

Ik denk dat dit…

  • is de snelste (zie benchmarks)
  • is echter gelijkmatig verdeeld, omdat Next() een willekeurige waarde van 31 bits is en er slechts 2^31 waarden worden geretourneerd. (50% van de aangrenzende waarden hebben dezelfde waarde)

Testen van de meeste functies op deze pagina:(i7, release, zonder debug, 2^28 loops)

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

Antwoord 4, autoriteit 4%

Ik heb het iets anders aangepakt dan anderen

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

De opmerkingen leggen uit wat ik doe. Haal het volgende dubbel, converteer dat getal naar een waarde tussen -1 en 1 en vermenigvuldig dat vervolgens met float.MaxValue.


Antwoord 5

Een andere oplossing is om dit te doen:

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

Antwoord 6

Hier is een andere manier die ik heb bedacht:
Stel dat u een float wilt tussen 5,5 en 7, met 3 decimalen.

float myFloat;
int myInt;
System.Random rnd = new System.Random();
void GenerateFloat()
{
myInt = rnd.Next(1, 2000);
myFloat = (myInt / 1000) + 5.5f;
}

Op die manier krijg je altijd een groter getal dan 5,5 en een kleiner getal dan 7.


Antwoord 7

Ik gebruik liever de volgende code om een ​​decimaal getal tot aan de eerste decimaal te genereren. je kunt de 3e regel kopiëren en plakken om meer getallen na de komma toe te voegen door dat getal toe te voegen in de tekenreeks “gecombineerd”. U kunt de minimum- en maximumwaarde instellen door de 0 en 9 te wijzigen in uw voorkeurswaarde.

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

Ik hoop dat het je heeft geholpen.

LEAVE A REPLY

Please enter your comment!
Please enter your name here

fifteen − nine =

Other episodes