Random float in C using getrandom

前端 未结 2 902
说谎
说谎 2021-02-19 15:35

I\'m trying to generate a random floating point number in between 0 and 1 (whether it\'s on [0,1] or [0,1) shouldn\'t matter for me). Every question online about this seems to i

2条回答
  •  [愿得一人]
    2021-02-19 16:25

    If you need to generate doubles, the following algorithm could be of use:

    CPython generates random numbers using the following algorithm (I changed the function name, typedefs and return values, but algorithm remains the same):

    double get_random_double() {
        uint32_t a = get_random_uint32_t() >> 5;
        uint32_t b = get_random_uint32_t() >> 6;
        return (a * 67108864.0 + b) * (1.0 / 9007199254740992.0);
    }
    

    The source of that algorithm is a Mersenne Twister 19937 random number generator by Takuji Nishimura and Makoto Matsumoto. Unfortunately the original link mentioned in the source is not available for download any longer.

    The comment on this function in CPython notes the following:

    [this function] is the function named genrand_res53 in the original code; generates a random number on [0,1) with 53-bit resolution; note that 9007199254740992 == 2**53; I assume they're spelling "/2**53" as multiply-by-reciprocal in the (likely vain) hope that the compiler will optimize the division away at compile-time. 67108864 is 2**26. In effect, a contains 27 random bits shifted left 26, and b fills in the lower 26 bits of the 53-bit numerator.

    The orginal code credited Isaku Wada for this algorithm, 2002/01/09


    Simplifying from that code, if you want to create a float fast, you should mask the bits of uint32_t with (1 << FLT_MANT_DIG) - 1 and divide by (1 << FLT_MANT_DIG) to get the proper [0, 1) interval:

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

    Since it can be assumed that your Linux has a C99 compiler, we can use ldexpf instead of that division:

    #include 
    
    result = ldexpf(r & ((1 << FLT_MANT_DIG) - 1), -FLT_MANT_DIG);
    

    To get the closed interval [0, 1], you can do the slightly less efficient

    result = ldexpf(r % (1 << FLT_MANT_DIG), -FLT_MANT_DIG);
    

    To generate lots of good quality random numbers fast, I'd just use the system call to fetch enough data to seed a PRNG or CPRNG, and proceed from there.

提交回复
热议问题