62

What is the best way to generate a random float in C#?

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

mskfisher
  • 3,291
  • 4
  • 35
  • 48
KrisTrip
  • 4,943
  • 12
  • 55
  • 74

7 Answers7

74

Best approach, no crazed values, distributed with respect to the representable intervals on the floating-point number line (removed "uniform" as with respect to a continuous number line it is decidedly non-uniform):

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 for subnormal floats

Warning: generates positive infinity as well! Choose exponent of 127 to be on the safe side.

Another approach which will give you some crazed values (uniform distribution of bit patterns), potentially useful for fuzzing:

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

An improvement over the previous version is this one, which does not create "crazed" values (neither infinities nor NaN) and is still fast (also distributed with respect to the representable intervals on the floating-point number line):

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 approach:

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 number line from: Intel Architecture Software Developer's Manual Volume 1: Basic Architecture. The Y-axis is logarithmic (base-2) because consecutive binary floating point numbers do not differ linearly.

Comparison of distributions, logarithmic Y-axis

D.R.
  • 20,268
  • 21
  • 102
  • 205
user7116
  • 63,008
  • 17
  • 141
  • 172
  • There doesn't seem to be a BitConverter.GetSingle method. Do you mean ToSingle? – KrisTrip Jul 29 '10 at 17:39
  • 2
    Using random bytes can easily end up with a NaN value, and the distribution may well be non-uniform. (I wouldn't like to predict the distribution.) – Jon Skeet Jul 29 '10 at 17:43
  • Agreed, the second one is useful for testing the range of potential floating point values including NaN. It appears this method produces about 0.25%-0.4% NaN. – user7116 Jul 29 '10 at 17:53
  • Interestingly, using Single.MinValue/Single.MaxValue as the range produces a pretty poor distribution of numbers. – user7116 Jul 29 '10 at 17:57
  • Actually, the second method produces a very nice distribution of floating point numbers (normal, denormal, positive, negative). The first method does not with the full range of values. – user7116 Jul 29 '10 at 18:00
  • @sixlettervariables: What sort of distribution do you deem "very nice"? In most cases I'd expect a reasonably uniform distribution to be desirable. – Jon Skeet Jul 29 '10 at 18:04
  • 2
    @sixlettervariables: The reason yours looks prettier that way is because you've given a logarithmic scale. On a linear scale, I believe NextDouble * range will be uniform. – Jon Skeet Jul 29 '10 at 18:27
  • NextDouble * Range lacks values below 1e35ish. I've produced an update way to create floats that are well distributed without NaN. – user7116 Jul 29 '10 at 18:39
  • @Jon Skeet: you are correct, however it has no values between -1e35 and 1e35 which makes it less than useful for testing. – user7116 Jul 29 '10 at 19:03
  • @sixlettervariables: *No* values in that range? I did a quick test and got about 30 per 100,000 samples - which is roughly what I'd expect, given the range of values available. – Jon Skeet Jul 29 '10 at 19:13
  • Another point would be the distance between two consecutive binary floating point numbers is not linear, but log2. I've updated by graphs to reflect this point. – user7116 Jul 29 '10 at 19:13
  • Perhaps I've just botched a copy of your code, but I cannot get your method to produce any values anywhere near 0. – user7116 Jul 29 '10 at 19:27
  • I think it would be worth specifying what the distribution is, rather than just asserting it's sane :) (In particular, it's *not* what I'd expect.) – Jon Skeet Jul 29 '10 at 19:55
30

Any reason not to use Random.NextDouble and then cast to float? That will give you a float between 0 and 1.

If you want a different form of "best" you'll need to specify your requirements. Note that Random shouldn't be used for sensitive matters such as finance or security - and you should generally reuse an existing instance throughout your application, or one per thread (as Random isn't thread-safe).

EDIT: As suggested in comments, to convert this to a range of 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: Now you've mentioned that this is for unit testing, I'm not sure it's an ideal approach. You should probably test with concrete values instead - making sure you test with samples in each of the relevant categories - infinities, NaNs, denormal numbers, very large numbers, zero, etc.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • I think NextDouble just provides values from 0.0 to 1.0 and I want more range than that (like from float.MinValue to float.MaxValue). Guess I should have specified :) – KrisTrip Jul 29 '10 at 17:31
  • 3
    Just multiply it by (MaxValue-MinValue) and add MinValue to it? So something like Random.NextDouble *(float.MaxValue-float.MinValue) + float.MinValue – Xzhsh Jul 29 '10 at 17:35
  • The distribution of values that produces is bimodal, I believe it has to do with how large the values are in the range. – user7116 Jul 29 '10 at 18:13
  • I've finally reasoned out why this method does not produce the expected distribution of values for a binary floating point number. If NextDouble() returns a uniform distribution between 0 and 1, only 10% of the values would be between 0 and 0.1. So only 10% of the time the numbers will be less than 1e37, which is not the expected distribution. – user7116 Jul 29 '10 at 19:34
  • @sixlettervariables: Where is this "expected distribution" specified? The distribution I expected was a uniform one, and I see no reason to believe that's not happening here. – Jon Skeet Jul 29 '10 at 19:54
  • Fair enough that the expected distribution was omitted, but I would gather a uniform distribution from MinValue to MaxValue would be desired. Since less floating point numbers can be represented the closer you get to MinValue or MaxValue, I would reason less values would be generated there, no? – user7116 Jul 29 '10 at 20:05
  • @sixlettervariables: That's not what I'd expect a uniform distribution to mean, no. I'd expect it to mean that the probability of a value within any given interval within the range is the same (as closely as possible) wherever that interval occurs. So the probability of getting a value in the range [0, 10000] is the same as the probability of getting a value in the range [100000, 110000]. I suppose it really depends on whether you view the floating point range as continuous or discrete. – Jon Skeet Jul 29 '10 at 20:44
  • Yes, I dusted off ye olde stats book and I'm in agreement for floating point range used as continuous values. – user7116 Jul 29 '10 at 21:07
  • Should I instead conclude I've produced a uniform distribution of bit patterns? – user7116 Jul 29 '10 at 21:25
  • @sixlettervariables: Yes, that would be reasonable - certainly for the last one. I don't think it's *quite* uniform for the rest, due to denormal numbers - but so long as the OP knows what he's getting by and large, that's probably okay :) – Jon Skeet Jul 29 '10 at 22:14
  • I am using both concrete and random values for unit testing. I test a range of concrete values and then create a number of random values to test with as well. – KrisTrip Jul 30 '10 at 13:41
  • @JonSkeet: How would you write this for double? There is no "bigger" floating point type than double to prevent the overflow when performing `double.MaxVavlue - double.MinValue`. EDIT: Is the correct solution to divide everything by 2 if necesseary? – D.R. Sep 22 '18 at 17:34
  • @D.R.: I'd probably generate one number in the range [0, double.MaxValue) and then generate the sign (i.e. negate it half the time). There are almost certainly better ways of doing it though. – Jon Skeet Sep 23 '18 at 07:29
  • Hm, doesn't work so good if min and max are not the same values. I'm using variable min and max values, so I would have to create the sign with some non 50:50 chance. Wondering if that's better than my divide scaling by two approach.. – D.R. Sep 23 '18 at 07:46
  • @D.R.: That's a different question :) Dividing by two to start with would probably be reasonable. It partly depends on just how stringent you want the guarantees to be. – Jon Skeet Sep 23 '18 at 16:13
5

One more version... (I think this one is pretty 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 2nd fastest (see benchmarks)
  • is evenly distributed

And One more version...(not as good but posting anyway)

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 benchmarks)
  • is evenly distributed however because Next() is a 31 bit random value it will only return 2^31 values. (50% of the neighbor values will have the same value)

Testing of most of the functions on this page: (i7, release, without 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
SunsetQuest
  • 8,041
  • 2
  • 47
  • 42
  • 1
    Are you sure that it will be faster because its a power of two? I would assume it would be implicitly cast to a float *(which wouldn't represent the value exactly, then use regular float division).* – ideasman42 Apr 16 '15 at 05:25
  • Thanks for noticing that, that was a good catch! You are right that it was converted to a float first so the compiler could not use a shift instruction. I removed the "is faster because it's a power of two". As you can tell I made several other changes including adding a new function and also doing benchmarks. – SunsetQuest Apr 20 '15 at 03:55
3

I took a slightly different approach than others

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

The comments explain what I'm doing. Get the next double, convert that number to a value between -1 and 1 and then multiply that with float.MaxValue.

Anthony Pegram
  • 123,721
  • 27
  • 225
  • 246
0

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
  • 132,049
  • 21
  • 248
  • 298
0

Here is another way that I came up with: Let's say you want to get a float 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;
}

That way you will always get a bigger number than 5.5 and a smaller number than 7.

mesrefoglu
  • 340
  • 2
  • 6
  • 20
0

I prefer using the following code to generate a decimal number up to fist decimal point. you can copy paste the 3rd line to add more numbers after decimal point by appending that number in string "combined". You can set the minimum and maximum value by changing the 0 and 9 to your preferred value.

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

I hope that it helped you.