How to generate random 64-bit unsigned integer in C
Asked Answered
M

7

17

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.

Matthia answered 8/10, 2015 at 8:3 Comment(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: math.sci.hiroshima-u.ac.jp/~m-mat/MT/VERSIONS/C-LANG/…Biforked
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 soonBiak
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.Airtoair
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 requirementsConstructive
@Biforked 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.Cellobiose
Check this link #20175648Hepato
Possible duplicate of How to generate a random number in C?Coatee
Possible duplicate of Getting big random numbers in C/C++Berdichev
@Berdichev 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.Twopenny
@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.Twopenny
T
9

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;
}
Twopenny answered 8/10, 2015 at 16:37 Comment(1)
This answer is like a poem. I mean the explanations. I totally understood everything related with my question.Cellobiose
T
6

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

Tetter answered 8/10, 2015 at 18:40 Comment(0)
H
2

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.

Heartwhole answered 8/10, 2015 at 9:7 Comment(1)
What is "hi-jinx"? =)Golding
E
2

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());
}
Einkorn answered 29/6, 2017 at 23:21 Comment(1)
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 <inttypes.h>)Twopenny
W
1

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

Wristwatch answered 8/10, 2015 at 18:4 Comment(1)
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.Twopenny
S
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;
}
Sailplane answered 8/10, 2015 at 9:22 Comment(3)
the thing is the OP's random value has only 30 bitsConstructive
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.Embranchment
@Embranchment 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.Twopenny
X
-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;
}
Xavier answered 8/10, 2015 at 8:24 Comment(5)
How does that satisfy the requirement for a random 64 bit unsigned int ?Communitarian
Well, I tried your code and printed out 1000 outcome. Results are like this; 04951651604868241121, 00651604895168241121, 03943165433604438241, 00160434265465541121... so I guess we can't have what I need with this method.Cellobiose
you mean,you don't want the leading zeros ?Xavier
leading zeros is also but, with this solution it is almost impossible to have a result like 00000000000000345432Cellobiose
i guess rand() isn't that flexibleXavier

© 2022 - 2024 — McMap. All rights reserved.