Search code examples
crandomunsigned-integer

How to generate random 64-bit unsigned integer in C


I need generate random 64-bit unsigned integers using C. I mean, the range should be 0 to 18446744073709551615. RAND_MAX is 1073741823.

I found some solutions in the links which might be possible duplicates but the answers mostly concatenates some rand() results or making some incremental arithmetic operations. So results are always 18 digits or 20 digits. I also want outcomes like 5, 11, 33387, not just 3771778641802345472.

By the way, I really don't have so much experience with the C but any approach, code samples and idea could be beneficial.


Solution

  • Concerning "So results are always 18 digits or 20 digits."

    See @Thomas comment. If you generate random numbers long enough, code will create ones like 5, 11 and 33387. If code generates 1,000,000,000 numbers/second, it may take a year as very small numbers < 100,000 are so rare amongst all 64-bit numbers.


    rand() simple returns random bits. A simplistic method pulls 1 bit at a time

    uint64_t rand_uint64_slow(void) {
      uint64_t r = 0;
      for (int i=0; i<64; i++) {
        r = r*2 + rand()%2;
      }
      return r;
    }
    

    Assuming RAND_MAX is some power of 2 - 1 as in OP's case 1073741823 == 0x3FFFFFFF, take advantage that 30 at least 15 bits are generated each time. The following code will call rand() 5 3 times - a tad wasteful. Instead bits shifted out could be saved for the next random number, but that brings in other issues. Leave that for another day.

    uint64_t rand_uint64(void) {
      uint64_t r = 0;
      for (int i=0; i<64; i += 15 /*30*/) {
        r = r*((uint64_t)RAND_MAX + 1) + rand();
      }
      return r;
    }
    

    A portable loop count method avoids the 15 /*30*/ - But see 2020 edit below.

    #if RAND_MAX/256 >= 0xFFFFFFFFFFFFFF
      #define LOOP_COUNT 1
    #elif RAND_MAX/256 >= 0xFFFFFF
      #define LOOP_COUNT 2
    #elif RAND_MAX/256 >= 0x3FFFF
      #define LOOP_COUNT 3
    #elif RAND_MAX/256 >= 0x1FF
      #define LOOP_COUNT 4
    #else
      #define LOOP_COUNT 5
    #endif
    
    uint64_t rand_uint64(void) {
      uint64_t r = 0;
      for (int i=LOOP_COUNT; i > 0; i--) {
        r = r*(RAND_MAX + (uint64_t)1) + rand();
      }
      return r;
    }
    

    The autocorrelation effects commented here are caused by a weak rand(). C does not specify a particular method of random number generation. The above relies on rand() - or whatever base random function employed - being good.

    If rand() is sub-par, then code should use other generators. Yet one can still use this approach to build up larger random numbers.


    [Edit 2020]

    Hallvard B. Furuseth provides as nice way to determine the number of bits in RAND_MAX when it is a Mersenne Number - a power of 2 minus 1.

    #define IMAX_BITS(m) ((m)/((m)%255+1) / 255%255*8 + 7-86/((m)%255+12))
    #define RAND_MAX_WIDTH IMAX_BITS(RAND_MAX)
    _Static_assert((RAND_MAX & (RAND_MAX + 1u)) == 0, "RAND_MAX not a Mersenne number");
    
    uint64_t rand64(void) {
      uint64_t r = 0;
      for (int i = 0; i < 64; i += RAND_MAX_WIDTH) {
        r <<= RAND_MAX_WIDTH;
        r ^= (unsigned) rand();
      }
      return r;
    }