14

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.

Erdi İzgi
  • 1,082
  • 1
  • 14
  • 29
  • 10
    **Don't** concatenate `rand()` as you'll have all sorts of autocorrelation effects, and the distribution will not be uniform. Take a look at these: http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/VERSIONS/C-LANG/c-lang.html – Bathsheba Oct 08 '15 at 08:07
  • 14
    `I also want outcomes like 5, 11, 33387` => there is 10 times more number between 1000000000000000000 and 9999999999999999999 than between 0 and 1000000000000000000... so don't expect to see numbers like 5 soon – Thomas Ayoub Oct 08 '15 at 08:09
  • 1
    You seem to be confused about base-10 digits (0...9), and bits (base-2 digits). Keep these separate in your thinking, for better understanding. – hyde Oct 08 '15 at 08:16
  • 1
    the probability you get 5 is just like the probability you get 3771778641802345472, which is equal to 1/2^64, a very very small number. So simply concatenating the bits works, unless you have some more strict requirements – phuclv Oct 08 '15 at 08:29
  • @Bathsheba I looked that page. Most of the links are broken. It looks outdated but I got the idea you wanted to give. Thomas You are so right, but as Lưu Vĩnh Phúc said I just want equal possibilities for both 5 and 3771778641802345472. And hyde You may right. I will try to edit something for better understanding. – Erdi İzgi Oct 08 '15 at 08:51
  • Check this link http://stackoverflow.com/questions/20175148/64bit-random-number-between-a-range – abhi312 Oct 08 '15 at 08:58
  • Possible duplicate of [How to generate a random number in C?](http://stackoverflow.com/questions/822323/how-to-generate-a-random-number-in-c) – AndersK Oct 08 '15 at 10:46
  • 2
    Possible duplicate of [Getting big random numbers in C/C++](http://stackoverflow.com/questions/28115724/getting-big-random-numbers-in-c-c) – nwellnhof Oct 08 '15 at 11:35
  • @nwellnhof A problem with that duplicate is that the best answer for C and C++ can differ. The accepted answer is a C one - for now -- but that post should have focused on 1 language, like this post. – chux - Reinstate Monica Oct 08 '15 at 17:00
  • @Erdi İzgi Suggest reviewing these answers, up-voting useful ones and accepting one. Might want to do that with many of your other questions too. – chux - Reinstate Monica Aug 12 '16 at 00:33

7 Answers7

6

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;
}
chux - Reinstate Monica
  • 113,725
  • 11
  • 107
  • 213
  • 2
    This answer is like a poem. I mean the explanations. I totally understood everything related with my question. – Erdi İzgi Oct 08 '15 at 22:36
4

If you don't need cryptographically secure pseudo random numbers, I would suggest using MT19937-64. It is a 64 bit version of Mersenne Twister PRNG.

Please, do not combine rand() outputs and do not build upon other tricks. Use existing implementation:

http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt64.html

Stas
  • 10,615
  • 6
  • 36
  • 55
1

Iff you have a sufficiently good source of random bytes (like, say, /dev/random or /dev/urandom on a linux machine), you can simply consume 8 bytes from that source and concatenate them. If they are independent and have a linear distribution, you're set.

If you don't, you MAY get away by doing the same, but there is likely to be some artefacts in your pseudo-random generator that gives a toe-hold for all sorts of hi-jinx.

Example code assuming we have an open binary FILE *source:

/* Implementation #1, slightly more elegant than looping yourself */
uint64_t 64bitrandom() 
{
  uint64_t rv;
  size_t count;

  do {
   count = fread(&rv, sizeof(rv), 1, source);
  } while (count != 1);
  return rv;
}

/* Implementation #2 */
uint64_t 64bitrandom()
{
  uint64_t rv = 0;
  int c;

  for (i=0; i < sizeof(rv); i++) {
     do {
       c = fgetc(source)
     } while (c < 0);
     rv = (rv << 8) | (c & 0xff);
  }
  return rv;
}

If you replace "read random bytes from a randomness device" with "get bytes from a function call", all you have to do is to adjust the shifts in method #2.

You're vastly more likely to get a "number with many digits" than one with "small number of digits" (of all the numbers between 0 and 2 ** 64, roughly 95% have 19 or more decimal digits, so really that is what you will mostly get.

Vatine
  • 18,928
  • 3
  • 50
  • 67
1

If you are willing to use a repetitive pseudo random sequence and you can deal with a bunch of values that will never happen (like even numbers? ... don't use just the low bits), an LCG or MCG are simple solutions. Wikipedia: Linear congruential generator can get you started (there are several more types including the commonly used Wikipedia: Mersenne Twister). And this site can generate a couple prime numbers for the modulus and the multiplier below. (caveat: this sequence will be guessable and thus it is NOT secure)

#include <stdio.h>
#include <stdint.h>

uint64_t
mcg64(void)
{
    static uint64_t i = 1;
    return (i = (164603309694725029ull * i) % 14738995463583502973ull);
}

int
main(int ac, char * av[])
{
    for (int i = 0; i < 10; i++)
        printf("%016p\n", mcg64());
}
duanev
  • 834
  • 12
  • 14
  • Note: the `ll` are not needed in the 2 constants. Better to use `"%016" PRIx64 "\n"` than `"%016p\n"` - insures a matching print specifier with `uint64_t`. (See ``) – chux - Reinstate Monica Aug 16 '18 at 15:58
0

If you have 32 or 16-bit random value - generate 2 or 4 randoms and combine them to one 64-bit with << and |.

uint64_t rand_uint64(void) {
    // Assuming RAND_MAX is 2^31.
    uint64_t r = rand();
    r = r<<30 | rand();
    r = r<<30 | rand();
    return r;
}
ESV
  • 7,280
  • 4
  • 36
  • 29
i486
  • 6,083
  • 3
  • 21
  • 37
  • 1
    the thing is the OP's random value has only 30 bits – phuclv Oct 08 '15 at 15:41
  • 1
    Not sure why this was downvoted. `uint64_t r = rand(); r = r<<30 | rand(); r = r<<30 | rand();` makes sense, if you know the value of `RAND_MAX` ahead of time. Wish I could push this up several answers. – ESV Mar 11 '18 at 16:36
  • @ESV The answer in flawed in the comment `// Assuming RAND_MAX is 2^31.` The answer makes sense if `RAND_MAX is 2^30 - 1`, as OP said. (i486 has wrong power-of-2, off-by-1). It could be even better with `r = r<<30 ^ rand();` (`^` vs `|`) as that would make sense for `RAND_MAX is 2^N - 1, N >= 30`, not just `N==30`. – chux - Reinstate Monica Aug 16 '18 at 16:12
0

I have tried this code here and it seems to work fine there.

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

int main(){
  srand(time(NULL));
  int a = rand();
  int b = rand();
  int c = rand();
  int d = rand();
  long e = (long)a*b;
  e = abs(e);
  long f = (long)c*d;
  f = abs(f);

  long long answer = (long long)e*f;

  printf("value %lld",answer);
  return 0;
}

I ran a few iterations and i get the following outputs :

value 1869044101095834648
value 2104046041914393000

value 1587782446298476296
value 604955295827516250
value 41152208336759610
value 57792837533816000

Jibin Mathew
  • 2,776
  • 1
  • 18
  • 37
  • 3
    Building up values with `*` as in `a*b` ruins the distribution of values generated. `(long)a*b` and `abs(e)` can both incur signed integer overflow - which is _undefined behavior_ (UB). `abs()` returns an `int`. Using `abs(some_long)` creates additional concerns when `int/long` range differ. – chux - Reinstate Monica Aug 16 '18 at 16:01
-1
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>

unsigned long long int randomize(unsigned long long int uint_64);

int main(void)
{
    srand(time(0));

    unsigned long long int random_number = randomize(18446744073709551615);

    printf("%llu\n",random_number);

    random_number = randomize(123);

    printf("%llu\n",random_number);

    return 0;

}

unsigned long long int randomize(unsigned long long int uint_64)
{
    char buffer[100] , data[100] , tmp[2];

    //convert llu to string,store in buffer
    sprintf(buffer, "%llu", uint_64);

    //store buffer length
    size_t len = strlen(buffer);

    //x : store converted char to int, rand_num : random number , index of data array
    int x , rand_num , index = 0;

    //condition that prevents the program from generating number that is bigger input value
    bool Condition = 0;

    //iterate over buffer array
    for( int n = 0 ; n < len ; n++ )
    {
        //store the first character of buffer
        tmp[0] = buffer[n];
        tmp[1] = '\0';

        //convert it to integer,store in x
        x = atoi(tmp);


        if( n == 0 )
        {
            //if first iteration,rand_num must be less than or equal to x
            rand_num = rand() % ( x + 1 );

            //if generated random number does not equal to x,condition is true
            if( rand_num != x )
                Condition = 1;

            //convert character that corrosponds to integer to integer and store it in data array;increment index
            data[index] = rand_num + '0';
            index++;
        }
        //if not first iteration,do the following
        else
        {
            if( Condition )
            {
                rand_num = rand() % ( 10 );

                data[index] = rand_num + '0';

                index++;
            }
            else
            {
                rand_num = rand() % ( x + 1 );

                if( rand_num != x )
                    Condition = 1;

                data[index] = rand_num + '0';

                index++;
            }
        }
    }

    data[index] = '\0';

    char *ptr ;

    //convert the data array to unsigned long long int
    unsigned long long int ret = _strtoui64(data,&ptr,10);

    return ret;
}
machine_1
  • 3,863
  • 2
  • 16
  • 39