How do you generate a random double uniformly distributed between 0 and 1 from C++?

后端 未结 13 607
暗喜
暗喜 2020-11-28 22:28

How do you generate a random double uniformly distributed between 0 and 1 from C++?

Of course I can think of some answers, but I\'d like to know what the standard pr

13条回答
  •  眼角桃花
    2020-11-28 23:11

    As I see it, there are three ways to go with this,

    1) The easy way.

    double rand_easy(void)
    {       return (double) rand() / (RAND_MAX + 1.0);
    }
    

    2) The safe way (standard conforming).

    double rand_safe(void)
    {
            double limit = pow(2.0, DBL_MANT_DIG);
            double denom = RAND_MAX + 1.0;
            double denom_to_k = 1.0;
            double numer = 0.0;
    
            for ( ; denom_to_k < limit; denom_to_k *= denom )
               numer += rand() * denom_to_k;
    
            double result = numer / denom_to_k;
            if (result == 1.0)
               result -= DBL_EPSILON/2;
            assert(result != 1.0);
            return result;
    }
    

    3) The custom way.

    By eliminating rand() we no longer have to worry about the idiosyncrasies of any particular version, which gives us more leeway in our own implementation.

    Note: Period of the generator used here is ≅ 1.8e+19.

    #define RANDMAX (-1ULL)
    uint64_t custom_lcg(uint_fast64_t* next)
    {       return *next = *next * 2862933555777941757ULL + 3037000493ULL;
    }
    
    uint_fast64_t internal_next;
    void seed_fast(uint64_t seed)
    {       internal_next = seed;
    }
    
    double rand_fast(void)
    {
    #define SHR_BIT (64 - (DBL_MANT_DIG-1))
            union {
                double f; uint64_t i;
            } u;
            u.f = 1.0;
            u.i = u.i | (custom_lcg(&internal_next) >> SHR_BIT);
            return u.f - 1.0;
    }
    

    Whatever the choice, functionality may be extended as follows,

    double rand_dist(double min, double max)
    {       return rand_fast() * (max - min) + min;
    }
    
    double rand_open(void)
    {       return rand_dist(DBL_EPSILON, 1.0);
    }
    
    double rand_closed(void)
    {       return rand_dist(0.0, 1.0 + DBL_EPSILON);
    }
    

    Final notes: The fast version - while written in C - may be adapted for use in C++ to be used as a replacement for std::generate_canonical, and will work for any generator emitting values with sufficient significant bits.

    Most 64 bit generators take advantage of their full width, so this can likely be used without modification (shift adjustment). e.g. this works as-is with the std::mt19937_64 engine.

提交回复
热议问题