The best method of generating random floating point numbers in C

 

Update: I want random floating point numbers from float.Minvalue to float.Maxvalue. I use these numbers in unit tests of some mathematical methods.

c# random floating-point

KrisTrip asked 2020-01-21T10:48:33Z

7 solutions

63 votes

The best method is that there is no crazy value, and the representable interval distribution relative to the floating-point number line (the deletion of "unity" is absolutely uneven relative to the continuous number line):

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 for abnormal buoys here

Warning: positive infinity will also be generated! For safety, please select an index of 127.

Another method will give you some crazy values (uniform distribution of bit patterns), which may be useful for fuzzy testing:

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

This version is an improvement over previous versions in that it does not create "Crazy" values (infinity and no NaN) and is still fast (i.e. the representable intervals on floating-point lines are distributed):

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

Least useful methods:

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

Floating point lines from: Intel Architecture Software Developer's manual, Volume 1: basic architecture. The Y-axis is logarithmic (based on 2) because there is no linear difference between consecutive binary floating-point numbers.

user7116 answered 2020-01-21T10:49:14Z

26 votes

Why not use float.MinValue and convert it to float.MaxValue? This will float you between 0 and 1.

If you want other forms of "best", you need to specify your requirements. Note that float.MinValue should not be used for sensitive transactions such as finance or security, and usually an existing instance should be reused throughout the application or in each thread (because float.MaxValue is not thread safe).

Edit: as suggested in the note, convert it to float.MinValue, and the range of 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;

Editor: now that you have mentioned that this is for unit testing, I'm not sure it's the ideal method. You should probably test with specific values instead - make sure to test with samples in each relevant category - infinity, NaN, irregular numbers, very large numbers, zero, etc.

Jon Skeet answered 2020-01-21T10:49:48Z

5 votes

There is another version... (I think this version is good)

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

I think this

  • Is the second fastest (see benchmark)
  • Uniform distribution

There is another version... (not very good, but still released)

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

I think this

  • Is the fastest (see benchmark)
  • Is evenly distributed, but because Next() is a 31 bit random value, it will return only 2 ^ 31 values. (50% of neighbor values will have the same value)

Test most functions on this page: (i7, release, without debugging, 2 ^ 28 cycles)

 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

Sunsetquest answered 2020-01-21T10:50:43Z

3 votes

My method is slightly different from other methods

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

These comments illustrate what I'm doing. Gets the next double, converts it to a value between - 1 and 1, and multiplies it by float.MaxValue.

Anthony Pegram answered 2020-01-21T10:51:08Z

0 votes

Another solution is to do this:

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

Simon Mourier answered 2020-01-21T10:51:28Z

0 votes

This is another method I came up with: suppose you want to get a floating point number between 5.5 and 7 (with 3 decimals).

float myFloat;
int myInt;
System.Random rnd = new System.Random();

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

In this way, you will always get a number greater than 5.5 and a number less than 7.

Qedized answered 2020-01-21T10:51:52Z

0 votes

I prefer to use the following code to generate a decimal number until the first decimal point. You can copy and paste the third line to add more numbers after the decimal point by appending the number to the string "combined". You can set the minimum and maximum values by changing 0 and 9 to preferred values.

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

translate from https://stackoverflow.com:/questions/3365337/best-way-to-generate-a-random-float-in-c-sharp

Keywords: C# unit testing Unity3d

Added by paul088 on Wed, 15 Sep 2021 04:52:32 +0300