C#中生成随机浮点数的最佳方法

 

更新:我想要从float.Minvalue到float.Maxvalue的随机浮点数。 我在一些数学方法的单元测试中使用这些数字。

c# random floating-point

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

7个解决方案

63 votes

最佳方法,没有疯狂的值,相对于浮点数线上的可表示间隔分布(相对于连续数线而言,“统一”被删除是绝对不均匀的):

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

(*)...检查此处是否有不正常的浮标

警告:也会产生正无穷大! 为安全起见,请选择127的指数。

另一种方法将为您提供一些疯狂的值(位模式的均匀分布),这可能对模糊测试很有用:

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

此版本比以前的版本有所改进,它不会创建“疯狂的”值(无穷大也没有NaN),并且速度仍然很快(也就浮点数行上的可表示间隔进行了分布):

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

最少有用的方法:

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

浮点数行来自:英特尔架构软件开发人员手册第1卷:基本架构。 Y轴是对数的(以2为底),因为连续的二进制浮点数没有线性差异。

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

26 votes

有什么理由不使用float.MinValue,然后转换为float.MaxValue? 这将使您在0到1之间浮动。

如果您想要其他形式的“最佳”,则需要指定您的要求。 请注意,不应将float.MinValue用于诸如财务或安全之类的敏感事务,并且通常应在整个应用程序中重用一个现有实例,或者在每个线程中重用一个实例(因为float.MaxValue并不是线程安全的)。

编辑:如注释中所建议,将其转换为float.MinValuefloat.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;

编辑:现在您已经提到这是用于单元测试的,我不确定这是理想的方法。 您可能应该改为使用具体值进行测试-确保使用每个相关类别中的样本进行测试-无穷大,NaN,非正规数,非常大的数,零等。

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

5 votes

还有一个版本...(我认为这个版本还不错)

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

我认为这...

  • 是第二快的(请参阅基准)
  • 分布均匀

还有一个版本...(不是很好,但仍然发布)

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

我认为这...

  • 是最快的(请参阅基准)
  • 是均匀分布的,但是因为Next()是一个31位随机值,它将仅返回2 ^ 31个值。 (50%的邻居值将具有相同的值)

测试此页面上的大多数功能:(i7,发布,不带调试,2 ^ 28个循环)

 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

我采取的方法与其他方法略有不同

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

这些评论说明了我在做什么。 获取下一个双精度数,将该数字转换为-1和1之间的值,然后将其乘以float.MaxValue

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

0 votes

另一个解决方案是这样做:

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

这是我想出的另一种方法:假设您想获得介于5.5和7之间的浮点数(带有3个小数)。

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

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

这样,您将始终获得大于5.5的数字而小于7的数字。

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

0 votes

我更喜欢使用下面的代码来生成一个十进制数字,直到第一个小数点。 您可以复制粘贴第三行以在小数点后添加更多数字,方法是将该数字附加在字符串“ combined”中。 您可以通过将0和9更改为首选值来设置最小值和最大值。

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值