如何在C语言中生成随机浮点数

55

我无法找到在[0,a]范围内生成随机浮点数的解决方案,其中a是由用户定义的某个浮点数。

我尝试了以下方法,但似乎没有正确地工作。

float x=(float)rand()/((float)RAND_MAX/a)

2
http://c-faq.com/lib/randrange.html - Ouadie
谢谢。应该是: float x=(float)rand()/((float)RAND_MAX/a) 还是 float x=(float)rand()/((float)RAND_MAX/a+1)? - fragon
3
投票重新开放,因为这2个重复的问题:1)关注C++解决方案;2)关注随机整数。 - chux - Reinstate Monica
可能是如何在C语言中生成随机数?的重复问题。 - Peter O.
1
@rid 不是的,那个链接只是回到了这里 :-D - vidstige
6个回答

77

试一试:

float x = (float)rand()/(float)(RAND_MAX/a);
要理解这是如何工作的,请考虑以下内容。
N = a random value in [0..RAND_MAX] inclusively.

上述方程(为了清晰起见去掉了转换)变为:

N/(RAND_MAX/a)

但是除以一个分数相当于乘以该分数的倒数,因此这等价于:

N * (a/RAND_MAX)

可以改写为:

a * (N/RAND_MAX)

考虑到N/RAND_MAX始终是0.0和1.0之间的浮点值,这将生成0.0和a之间的值。

或者,您可以使用以下方法,它有效地执行了我上面展示的拆分。实际上,我更喜欢这个方法,因为对于我来说,它更清晰明了:

float x = ((float)rand()/(float)(RAND_MAX)) * a;

注意:浮点数表示的a必须是精确的,否则它永远不会触发您的绝对边界情况(它会接近但不会达到)。请参阅此文章以获取有关原因的详细信息。

示例

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main(int argc, char *argv[])
{
    srand((unsigned int)time(NULL));

    float a = 5.0;
    for (int i=0;i<20;i++)
        printf("%f\n", ((float)rand()/(float)(RAND_MAX)) * a);
    return 0;
}

输出

1.625741
3.832026
4.853078
0.687247
0.568085
2.810053
3.561830
3.674827
2.814782
3.047727
3.154944
0.141873
4.464814
0.124696
0.766487
2.349450
2.201889
2.148071
2.624953
2.578719

21
这可能会有非常糟糕的舍入特性。rand()返回一个32位整数,你将其强制转换为一个32位浮点数,这将导致值被量化。例如,你获得值1000000161到1000000223都会四舍五入成1000000192,因此获得值1000000192的概率比获得值1高64倍。你可以避免这个问题,通过先将其转换为double,在最后将其再转换为float:float((double)rand()/(double)(RAND_MAX/a))。 - Glenn Maynard
3
在我看来,最清晰的形式是 **a * (N/RAND_MAX)**,因为 N/RAND_MAX 是介于 0 和 1 之间的随机数。 - Zibri
@GlennMaynard:如果你要除以(RAND_MAX+1ULL)(可能是2的幂),那么你只需缩放FP指数。 实际上需要一些舍入:如果您想生成统一的浮点值[0..1],但仍有机会生成每个可表示的浮点数,则需要多个整数值舍入为范围的0.5,1部分相同的最终浮点数。 指数越小,每个可表示的浮点数越接近,这就是为什么不能仅使用rand来生成FP位模式(不会均匀)的原因。 - Peter Cordes
除非您只使用约24位的熵,以便每个可能的输出均匀分布(例如生成[1,2],然后减去1.0),否则对于每个获得特定非常小的数字的时间,您需要多次在0.5、1范围内获得数字,以使分布均匀。不过,将RAND_MAX除以RAND_MAX+1而不是除以RAND_MAX可能意味着从rand转换为double存在一些好处或至少有所不同。(使用double,您可能具有足够的精度来使用乘法逆而不是实际除法。) - Peter Cordes

33

你也可以使用类似以下的方式在范围[min,max]内生成:

float float_rand( float min, float max )
{
    float scale = rand() / (float) RAND_MAX; /* [0, 1.0] */
    return min + scale * ( max - min );      /* [min, max] */
}

3

这将生成两个浮点数之间的随机浮点数。

float RandomFloat(float min, float max){
   return ((max - min) * ((float)rand() / RAND_MAX)) + min;
}

3

虽然现在可能并不重要,但这是一个生成在两个值之间浮点数的函数。

#include <math.h>

float func_Uniform(float left, float right) {
    float randomNumber = sin(rand() * rand());
    return left + (right - left) * fabs(randomNumber);
}

2
不错的技巧,也是唯一一个处理通过缩放随机数€ [0..RAND_MAX] € |N引入量化的方法。然而,两个随机数的乘积和正弦函数的乘积会导致非均匀分布。这个问题有没有什么解决办法呢?https://math.stackexchange.com/questions/456250/probability-distribution-for-product-of-two-random-numbers - avans

2
如果您想在一定范围内生成随机浮点数,请尝试下面的解决方案。
#include <stdio.h>
#include <stdlib.h>
#include <time.h>


float
random_float(const float min, const float max)
{
    if (max == min) return min;
    else if (min < max) return (max - min) * ((float)rand() / RAND_MAX) + min;

    // return 0 if min > max
    return 0;
}


int
main (const int argc, const char *argv[])
{
    srand(time(NULL));

    char line[] = "-------------------------------------------";

    float data[10][2] = {
        {-10, 10},
        {-5., 5},
        {-1, 1},
        {-0.25, -0.15},
        {1.5, 1.52},
        {-1700, 8000},
        {-0.1, 0.1},
        {-1, 0},
        {-1, -2},
        {1.2, 1.1}
    };

    puts(line);
    puts("     From    |    Result    |      To");
    puts(line);


    int i;
    for (i = 0; i < 10; ++i) {
        printf("%12f | %12f | %12f\n", data[i][0], random_float(data[i][0], data[i][1]), data[i][1]);
    }

    puts(line);

    return 0;
}

一个结果(数值是善变的)
-------------------------------------------
     From    |    Result    |      To
-------------------------------------------
  -10.000000 |     2.330828 |    10.000000
   -5.000000 |    -4.945523 |     5.000000
   -1.000000 |     0.004242 |     1.000000
   -0.250000 |    -0.203197 |    -0.150000
    1.500000 |     1.513431 |     1.520000
-1700.000000 |  3292.941895 |  8000.000000
   -0.100000 |    -0.021541 |     0.100000
   -1.000000 |    -0.148299 |     0.000000
   -1.000000 |     0.000000 |    -2.000000
    1.200000 |     0.000000 |     1.100000
-------------------------------------------

-4
也许可以这样做: a = Convert.ToDouble(rand.Next(-1000, 1000)) / 100; 这样我们就可以生成从-10.00到10.00的数字。

那看起来不像是C语言。 - Steve Summit

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接