Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generating a Random Decimal in C#

How can I get a random System.Decimal? System.Random doesn't support it directly.

like image 447
Daniel Ballinger Avatar asked Mar 04 '09 07:03

Daniel Ballinger


People also ask

How do you generate random decimals?

1. Select a blank cell, and type =RAND() into it, and then drag the fill handle to fill the range you need with the formula. 2. Then select the range you have applied the formula, and click the Increase Decimal button or Decrease Decimal button under Home tab to specify the decimal numbers.

How do you generate random decimal numbers in C++?

We can use srand () and rand () function to generate random numbers between 0 and 1. Note that we have to cast the output of rand () function to the decimal value either float or double.

What does rand () do in C?

Description. The C library function int rand(void) returns a pseudo-random number in the range of 0 to RAND_MAX. RAND_MAX is a constant whose default value may vary between implementations but it is granted to be at least 32767.

How do you generate a random floating point number?

Use a random. random() function of a random module to generate a random float number uniformly in the semi-open range [0.0, 1.0) . Note: A random() function can only provide float numbers between 0.1. to 1.0. Us uniform() method to generate a random float number between any two numbers.


2 Answers

EDIT: Removed old version

This is similar to Daniel's version, but will give the complete range. It also introduces a new extension method to get a random "any integer" value, which I think is handy.

Note that the distribution of decimals here is not uniform.

/// <summary> /// Returns an Int32 with a random value across the entire range of /// possible values. /// </summary> public static int NextInt32(this Random rng) {      int firstBits = rng.Next(0, 1 << 4) << 28;      int lastBits = rng.Next(0, 1 << 28);      return firstBits | lastBits; }  public static decimal NextDecimal(this Random rng) {      byte scale = (byte) rng.Next(29);      bool sign = rng.Next(2) == 1;      return new decimal(rng.NextInt32(),                          rng.NextInt32(),                         rng.NextInt32(),                         sign,                         scale); } 
like image 157
Jon Skeet Avatar answered Sep 23 '22 18:09

Jon Skeet


You would normally expect from a random-number-generator that it not only generated random numbers, but that the numbers were uniformly randomly generated.

There are two definitions of uniformly random: discrete uniformly random and continuous uniformly random.

Discretely uniformly random makes sense for a random number generator that has a finite number of different possible outcomes. For example generating an integer between 1 and 10. You would then expect that the probability of getting 4 is the same as getting 7.

Continuously uniformly random makes sense when the random number generator generates numbers in a range. For example a generator that generates a real number between 0 and 1. You would then expect that the probability of getting a number between 0 and 0.5 is the same as getting a number between 0.5 and 1.

When a random number generator generates floating-point numbers (which is basically what a System.Decimal is - it is just floating-point with base 10), it is arguable what the proper definition of uniformly random is:

On one hand, since the floating-point number is being represented by a fixed number of bits in a computer, it is obvious that there are a finite number of possible outcomes. So one could argue that the proper distribution is a discrete continuous distribution with each representable number having the same probability. That is basically what Jon Skeet's and John Leidegren's implementation does.

On the other hand, one might argue that since a floating-point number is supposed to be an approximation to a real number, we would be better off by trying to approximate the behavior of a continuous random number generator - even though are actual RNG is actually discrete. This is the behavior you get from Random.NextDouble(), where - even though there are approximately as many representable numbers in the range 0.00001-0.00002 as there are in the range 0.8-0.9, you are a thousand times more likely to get a number in the second range - as you would expect.

So a proper implementation of a Random.NextDecimal() should probably be continuously uniformly distributed.

Here is a simple variation of Jon Skeet's answer that is uniformly distributed between 0 and 1 (I reuse his NextInt32() extension method):

public static decimal NextDecimal(this Random rng) {      return new decimal(rng.NextInt32(),                          rng.NextInt32(),                         rng.Next(0x204FCE5E),                         false,                         0); } 

You could also discuss how to get an uniform distribution over the entire range of decimals. There is probably an easier way to do this, but this slight modification of John Leidegren's answer should produce a relatively uniform distribution:

private static int GetDecimalScale(Random r) {   for(int i=0;i<=28;i++){     if(r.NextDouble() >= 0.1)       return i;   }   return 0; }  public static decimal NextDecimal(this Random r) {     var s = GetDecimalScale(r);     var a = (int)(uint.MaxValue * r.NextDouble());     var b = (int)(uint.MaxValue * r.NextDouble());     var c = (int)(uint.MaxValue * r.NextDouble());     var n = r.NextDouble() >= 0.5;     return new Decimal(a, b, c, n, s); } 

Basically, we make sure that values of scale are chosen proportionally to the size of the corresponding range.

That means that we should get a scale of 0 90% of the time - since that range contains 90% of the possible range - a scale of 1 9% of the time, etc.

There are still some problems with the implementation, since it does not take into account that some numbers have multiple representations - but it should be much closer to a uniform distribution than the other implementations.

like image 45
Rasmus Faber Avatar answered Sep 23 '22 18:09

Rasmus Faber