在C中生成范围(-0.5,0.5)之间的随机数

Generate a random number between range (-0.5 , 0.5) in C

考虑以下 C 程序:

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

int main()
{
    srand(time(NULL));

    for (int i = 0; i < 20; ++i) {

        static float a = 0;
        a = (rand() % 2001 - 1000) / 2.e3;
        printf("%.4f ", a);
    }
}

这将成功生成然后打印 -0.5 到 0.5 之间的随机数列表。例如:

./main.out
-0.2475 -0.3640 -0.3945 0.2995 0.0460 0.2230 -0.0340 0.1630 -0.2275 -0.3450 0.3560 -0.4335 -0.0025 -0.2980 -0.0505 -0.1815 0.3115 -0.4465 -0.1325 -0.2340

我检查了运算符的优先级,但仍然不明白表达式 (rand()%2001 - 1000)/2.e3 的工作原理。

(特别是负号是从哪里来的)

 a = (rand() % 2001 - 1000) / 2.e3;

rand()先计算

    a = (rand() % 2001 - 1000) / 2.e3;
         ^^^^^^ int between `0` and `RAND_MAX` inclusive

然后模运算符生成一个介于 02000 之间的值。

    a = ([0 .. 2000] - 1000) / 2.e3;
         ^^^^^^^^^^^ int

然后该值减去 1000,成为 -10001000

之间的值
    a = ([-1000 .. 1000]) / 2.e3;
         ^^^^^^^^^^^^^^^ int

然后,除以 2000.0(或 2.e3)在 -0.50.5

之间生成 double
    a = [-0.5 .. 0.5];
        ^^^^^^^^^^^^^ double

我们来分析一下表达式(rand() % 2001 - 1000) / 2.e3:

  • <stdlib.h> 中定义的 rand() 函数 returns int 类型的伪随机整数,范围在 0RAND_MAX包括在内。 RAND_MAX 也是在 <stdlib.h> 中定义的常量,其值至少为 32767.

  • rand() % 2001 计算除以 2001 的余数。由于 rand() 为正数,因此结果是 02000 范围内的伪随机数,由于 2001 不除以 RAND_MAX 而造成小偏差均匀。

  • rand() % 2001 - 1000 被计算为 (rand() % 2001) - 1000,结果的范围向负数移动 1000,即在 -10001000 之间包括在内。

  • 来评估(rand() % 2001 - 1000) / 2.e3,将前面步骤得到的值转换为类型double并除以2.e3,这将更具可读性2000.0。因此,结果是 double 类型的浮点值,在 -0.50.5 之间包含 2001 个可能的不同值。

  • a = (rand() % 2001 - 1000) / 2.e3; 将此 double 值转换为 float,即 a 的类型。 float 值在传递给 printf 时将隐式转换回类型 double,这种转换在许多情况下不会产生完全相同的数字。

  • 请注意,没有理由将 a 定义为 static 变量。

这是一个替代实现,它在相同的包含范围内产生更多不同的值,并且分布偏差略小:

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

int main() {
    srand(time(NULL));

    for (int i = 0; i < 20; ++i) {
        double a = rand() / (double)RAND_MAX - 0.5;
        printf("%f ", a);
    }
    printf("%\n");
    return 0;
}