Generating a Random Decimal in C#

2020-01-26 07:05发布

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

12条回答
爷的心禁止访问
2楼-- · 2020-01-26 08:00

I wanted to generate "random" decimals up to 9 decimal places. My approach was to just generate a double and divide it for the decimals.

int randomInt = rnd.Next(0, 100);

double randomDouble = rnd.Next(0, 999999999);
decimal randomDec = Convert.ToDecimal(randomint) + Convert.ToDecimal((randomDouble/1000000000));

the "randomInt" is the number before the decimal place, you could just put 0. To reduce decimal points simply remove "9"s in random and "0"s in dividing

查看更多
男人必须洒脱
3楼-- · 2020-01-26 08:02

I know this is an old question, but the distribution issue Rasmus Faber described kept bothering me so I came up with the following. I have not looked in depth at the NextInt32 implementation provided by Jon Skeet and am assuming (hoping) it has the same distribution as Random.Next().

//Provides a random decimal value in the range [0.0000000000000000000000000000, 0.9999999999999999999999999999) with (theoretical) uniform and discrete distribution.
public static decimal NextDecimalSample(this Random random)
{
    var sample = 1m;
    //After ~200 million tries this never took more than one attempt but it is possible to generate combinations of a, b, and c with the approach below resulting in a sample >= 1.
    while (sample >= 1)
    {
        var a = random.NextInt32();
        var b = random.NextInt32();
        //The high bits of 0.9999999999999999999999999999m are 542101086.
        var c = random.Next(542101087);
        sample = new Decimal(a, b, c, false, 28);
    }
    return sample;
}

public static decimal NextDecimal(this Random random)
{
    return NextDecimal(random, decimal.MaxValue);
}

public static decimal NextDecimal(this Random random, decimal maxValue)
{
    return NextDecimal(random, decimal.Zero, maxValue);
}

public static decimal NextDecimal(this Random random, decimal minValue, decimal maxValue)
{
    var nextDecimalSample = NextDecimalSample(random);
    return maxValue * nextDecimalSample + minValue * (1 - nextDecimalSample);
}
查看更多
贪生不怕死
4楼-- · 2020-01-26 08:02

I puzzled with this for a bit. This is the best I could come up with:

public class DecimalRandom : Random
    {
        public override decimal NextDecimal()
        {
            //The low 32 bits of a 96-bit integer. 
            int lo = this.Next(int.MinValue, int.MaxValue);
            //The middle 32 bits of a 96-bit integer. 
            int mid = this.Next(int.MinValue, int.MaxValue);
            //The high 32 bits of a 96-bit integer. 
            int hi = this.Next(int.MinValue, int.MaxValue);
            //The sign of the number; 1 is negative, 0 is positive. 
            bool isNegative = (this.Next(2) == 0);
            //A power of 10 ranging from 0 to 28. 
            byte scale = Convert.ToByte(this.Next(29));

            Decimal randomDecimal = new Decimal(lo, mid, hi, isNegative, scale);

            return randomDecimal;
        }
    }

Edit: As noted in the comments lo, mid and hi can never contain int.MaxValue so the complete range of Decimals isn't possible.

查看更多
相关推荐>>
5楼-- · 2020-01-26 08:08

Here is Decimal random with Range implementation that works fine for me.

public static decimal NextDecimal(this Random rnd, decimal from, decimal to)
{
    byte fromScale = new System.Data.SqlTypes.SqlDecimal(from).Scale;
    byte toScale = new System.Data.SqlTypes.SqlDecimal(to).Scale;

    byte scale = (byte)(fromScale + toScale);
    if (scale > 28)
        scale = 28;

    decimal r = new decimal(rnd.Next(), rnd.Next(), rnd.Next(), false, scale);
    if (Math.Sign(from) == Math.Sign(to) || from == 0 || to == 0)
        return decimal.Remainder(r, to - from) + from;

    bool getFromNegativeRange = (double)from + rnd.NextDouble() * ((double)to - (double)from) < 0;
    return getFromNegativeRange ? decimal.Remainder(r, -from) + from : decimal.Remainder(r, to);
}
查看更多
等我变得足够好
6楼-- · 2020-01-26 08:08

Check out the following link for ready-made implementations that should help:

MathNet.Numerics, Random Numbers and Probability Distributions

The extensive distributions are especially of interest, built on top of the Random Number Generators (MersenneTwister, etc.) directly derived from System.Random, all providing handy extension methods (e.g. NextFullRangeInt32, NextFullRangeInt64, NextDecimal, etc.). You can, of course, just use the default SystemRandomSource, which is simply System.Random embellished with the extension methods.

Oh, and you can create your RNG instances as thread safe if you need it.

Very handy indeed!

This is an old question, but for those who are just reading it, why re-invent the wheel?

查看更多
来,给爷笑一个
7楼-- · 2020-01-26 08:11

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);
}
查看更多
登录 后发表回答