使用getrandom实现C中的随机浮点

使用getrandom实现C中的随机浮点,c,random,floating-point,C,Random,Floating Point,我试图生成一个介于0和1之间的随机浮点数(无论它是在[0,1]还是[0,1]上,对我来说都不重要)。在线上关于这一点的每一个问题似乎都涉及到rand()调用,以时间(NULL)为种子,但我希望能够每秒调用我的程序不止一次,每次都能得到不同的随机数。这让我想到了Linux中的getrandom系统调用,它来自/dev/uradom。我想到了这个: #include <stdio.h> #include <sys/syscall.h> #include <unistd.

我试图生成一个介于0和1之间的随机浮点数(无论它是在[0,1]还是[0,1]上,对我来说都不重要)。在线上关于这一点的每一个问题似乎都涉及到
rand()
调用,以
时间(NULL)为种子
,但我希望能够每秒调用我的程序不止一次,每次都能得到不同的随机数。这让我想到了Linux中的getrandom系统调用,它来自/dev/uradom。我想到了这个:

#include <stdio.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <stdint.h>

int main() {
  uint32_t r = 0;
  for (int i = 0; i < 20; i++) {
    syscall(SYS_getrandom, &r, sizeof(uint32_t), 0);
    printf("%f\n", ((double)r)/UINT32_MAX);
  }
  return 0;
}
#包括
#包括
#包括
#包括
int main(){
uint32_t r=0;
对于(int i=0;i<20;i++){
syscall(SYS\u getrandom,&r,sizeof(uint32\u t),0);
printf(“%f\n”,((双)r)/UINT32_MAX);
}
返回0;
}

我的问题只是我是否正确地执行了此操作。它似乎有效,但我担心我误用了某些东西,而且几乎没有使用getrandom()online的示例。

如果需要生成双精度,可以使用以下算法:

CPython使用以下算法(我更改了函数名、typedefs和返回值,但算法保持不变):

该算法的源代码是西村拓二和松本Makoto的Mersenne Twister 19937随机数生成器。不幸的是,源代码中提到的原始链接不再可供下载

CPython中关于此函数的注释说明如下:

[此函数]是原始代码中名为genrand_res53的函数; 在[0,1]上生成一个53位分辨率的随机数;注意
9007199254740992==2**53
;我假设他们将“
/2**53
”拼写为 乘以倒数(可能是徒劳的),希望编译器 在编译时优化除法。
67108864
2**26
效果是,a包含27个左移26位的随机位,
b
填充 53位分子的下26位

该算法的原始代码为伊萨库·瓦达(Isaku Wada),2002/01/09


从该代码简化,如果要创建
浮点
快速,应使用
(1OP有两个问题:

  • 如何非常随机地开始序列

  • 如何在[0…1)范围内生成一个
    double

  • 通常的方法是获取一个非常随机的源,比如
    /dev/uradom
    ,或者从
    syscall()
    或者甚至可能
    seed=time()^process\u id;
    通过
    srand()
    进行seed。然后根据需要调用
    rand()

    下面包含了一个快速转换的方法来生成一个统一的
    [0.0到1.0)
    (线性分布)。但是像所有的随机生成函数一样,真正好的生成函数是基于广泛的研究的。这个函数只是基于
    DBL\u MANT\u DIG
    rand\u MAX
    调用
    几次
    rand()

    [编辑]原始的
    double rand\u 01(void)
    有一个缺点,它只生成2^52个不同的
    double
    s,而不是2^53。它已被修改。备选方案:一个
    double
    版本的
    rand\u 01\u ld(void)
    如下所示

    #include <assert.h>
    #include <float.h>
    #include <limits.h>
    #include <stdint.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    
    double rand_01(void) {
      assert(FLT_RADIX == 2); // needed for DBL_MANT_DIG
      unsigned long long limit = (1ull << DBL_MANT_DIG) - 1;
      double r = 0.0;
      do {
        r += rand();
        // Assume RAND_MAX is a power-of-2 - 1
        r /= (RAND_MAX/2 + 1)*2.0;
        limit = limit / (RAND_MAX/2 + 1) / 2;
      } while (limit);
    
      // Use only DBL_MANT_DIG (53) bits of precision.
      if (r < 0.5) {
        volatile double sum = 0.5 + r;
        r = sum - 0.5;
      }
      return r;
    }
    
    int main(void) {
      FILE *istream = fopen("/dev/urandom", "rb");
      assert(istream);
      unsigned long seed = 0;
      for (unsigned i = 0; i < sizeof seed; i++) {
        seed *= (UCHAR_MAX + 1);
        int ch = fgetc(istream);
        assert(ch != EOF);
        seed += (unsigned) ch;
      }
      fclose(istream);
      srand(seed);
    
      for (int i=0; i<20; i++) {
        printf("%f\n", rand_01());
      }
    
      return 0;
    }
    
    #包括
    #包括
    #包括
    #包括
    #包括
    #包括
    #包括
    双随机变量01(无效){
    assert(FLT_RADIX==2);//DBL_MANT_DIG需要
    
    unsigned long long limit=(1ll而不是
    syscall()
    ,您是否可以打开
    fopen('/dev/uradom',rb')
    并读取4个字节?或者将其放入
    srand()
    ?一种更方便的方法是打开
    /dev/uradom
    读取(2)
    来自它。@user3030010,是什么让你认为系统调用比从Uradom设备读取更不容易失败?如果用随机位填充double或float,你可以生成NaN或无穷大,但没有人建议这样做。如果生成一个填充了随机位的无符号整数,请转换为
    double
    ,然后除以最大可能值,则在闭合间隔[0,1]内可靠地得到一个
    双精度@tofro S/他说的不是重复播种,而是重复运行程序,程序在启动时播种一次。我在想,从更多随机位开始比结果有尾数位对精度的影响。为什么不在合适的整数中生成
    DBL_尾数_位
    随机位,将其转换为
    double
    ,并使用
    ldexp()
    将其缩放到范围
    [0,1)
    ?@John Bollinger挑战在于“生成DBL\u尾数\u位随机位”在
    RAND\u MAX
    DBL\u MANT\u DIG
    方面以可移植的方式。当然希望代码在编译时知道
    log2(RAND\u MAX+1)
    的整数常量,而不存在任何范围错误或其他问题。即使
    RAND\u MAX+1u
    也可能溢出。任何想法都值得赞赏。@John Bollinger添加了第二种方法。@(对于
    长双精度
    )不使用额外的随机位。很好。如果可以的话,我会再给你一个+1。另一篇信息性文章。有什么反对
    float.h
    limits.h
    的吗?
    #include <stdio.h>
    #include <sys/syscall.h>
    #include <unistd.h>
    #include <stdint.h>
    #include <float.h>
    
    int main() {
        uint32_t r = 0;
        float result;
        for (int i = 0; i < 20; i++) {
            syscall(SYS_getrandom, &r, sizeof(uint32_t), 0);
            result = (float)(r & ((1 << FLT_MANT_DIG) - 1)) / (1 << FLT_MANT_DIG);
            printf("%f\n", result);
        }
        return 0;
    }
    
    #include <math.h>
    
    result = ldexpf(r & ((1 << FLT_MANT_DIG) - 1), -FLT_MANT_DIG);
    
    result = ldexpf(r % (1 << FLT_MANT_DIG), -FLT_MANT_DIG);
    
    #include <assert.h>
    #include <float.h>
    #include <limits.h>
    #include <stdint.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    
    double rand_01(void) {
      assert(FLT_RADIX == 2); // needed for DBL_MANT_DIG
      unsigned long long limit = (1ull << DBL_MANT_DIG) - 1;
      double r = 0.0;
      do {
        r += rand();
        // Assume RAND_MAX is a power-of-2 - 1
        r /= (RAND_MAX/2 + 1)*2.0;
        limit = limit / (RAND_MAX/2 + 1) / 2;
      } while (limit);
    
      // Use only DBL_MANT_DIG (53) bits of precision.
      if (r < 0.5) {
        volatile double sum = 0.5 + r;
        r = sum - 0.5;
      }
      return r;
    }
    
    int main(void) {
      FILE *istream = fopen("/dev/urandom", "rb");
      assert(istream);
      unsigned long seed = 0;
      for (unsigned i = 0; i < sizeof seed; i++) {
        seed *= (UCHAR_MAX + 1);
        int ch = fgetc(istream);
        assert(ch != EOF);
        seed += (unsigned) ch;
      }
      fclose(istream);
      srand(seed);
    
      for (int i=0; i<20; i++) {
        printf("%f\n", rand_01());
      }
    
      return 0;
    }
    
    long double rand_01_ld(void) {
      // These should be calculated once rather than each function call
      // Leave that as a separate implementation problem
      // Assume RAND_MAX is power-of-2 - 1
      assert((RAND_MAX & (RAND_MAX + 1U)) == 0);
      double rand_max_p1 = (RAND_MAX/2 + 1)*2.0;
      unsigned BitsPerRand = (unsigned) round(log2(rand_max_p1));
      assert(FLT_RADIX != 10);
      unsigned BitsPerFP = (unsigned) round(log2(FLT_RADIX)*LDBL_MANT_DIG);
    
      long double r = 0.0;
      unsigned i;
      for (i = BitsPerFP; i >= BitsPerRand; i -= BitsPerRand) {
        r += rand();
        r /= rand_max_p1;
      }
      if (i) {
        r += rand() % (1 << i);
        r /= 1 << i;
      }
      return r;
    }