1

I'm hoping that somebody can give me an understanding of why the code works the way it does. I'm trying to wrap my head around things but am lost.

My professor has given us this code snippet which we have to use in order to generate random numbers in C. The snippet in question generates a 64-bit integer, and we have to adapt it to also generate 32-bit, 16-bit, and 8-bit integers. I'm completely lost on where to start, and I'm not necessarily asking for a solution, just on how the original snippet works, so that I can adapt it form there.

long long rand64()
{
 int a, b;
 long long r;
 a = rand();
 b = rand();
 r = (long long)a;
 r = (r << 31) | b;
 return r;
}

Questions I have about this code are:

  1. Why is it shifted 31 bits? I thought rand() generated a number between 0-32767 which is 16 bits, so wouldn't that be 48 bits?
  2. Why do we say | (or) b on the second to last line?
Christopher Moore
  • 15,626
  • 10
  • 42
  • 52
JohnnyLeek
  • 160
  • 10

2 Answers2

1

I'm making the relatively safe assumption that, in your computer's C implementation, long long is a 64-bit data type.

The key here is that, since long long r is signed, any value with the highest bit set will be negative. Therefore, the code shifts r by 31 bits to avoid setting that bit.

The | is a logical bit operator which combines the two values by setting all of the bits in r which are set in b.

EDIT:

After reading some of the comments, I realized that my answer needs correction. rand() returns a value no more than RAND_MAX which is typically 2^31-1. Therefore, r is a 31-bit integer. If you shifted it 32 bits to the left, you'd guarantee that its 31st bit (0-up counting) would always be zero.

Daniel Walker
  • 6,380
  • 5
  • 22
  • 45
  • Holy cow, that makes so much more sense! So for a random 16 bit integer, could I just call rand() once and then shift it 16 bits to the left? And a similar story for 8-bit? – JohnnyLeek Sep 22 '20 at 23:15
  • Yes lol I was thinking about the professors implementation in my head as I was typing. But that would be correct? – JohnnyLeek Sep 22 '20 at 23:37
  • Yeah. You can also mask off the bits you don't want. So, `uint16_t value=rand()&0x0000ffff;` – Daniel Walker Sep 22 '20 at 23:38
  • In fact, storing `rand()` in a `uint16_t` may do that masking automatically but don't quote me on that. – Daniel Walker Sep 22 '20 at 23:40
  • Thank you for all the help! I really do appreciate it. Slowly (ever so slowly), C is becoming a far less daunting language. (I know this isn't just related to C but I suppose I'm referring to the lower level aspects of Computer Science in general) – JohnnyLeek Sep 22 '20 at 23:55
  • 1
    Glad to hear it. =) – Daniel Walker Sep 22 '20 at 23:57
  • 1
    `long long` **must** have at least 64 bits according to the standard. But here the function doesn't even fill the 63th bit, let alone the sign bit. In fact if a random value in the int64_t range is needed then negative values must appear in the same frequency as non-negative values. There's no reason to avoid signed values – phuclv Sep 23 '20 at 01:31
0

rand() generates a random value [0...RAND_MAX] of questionable repute - but let us set that reputation aside and assume rand() is good enough and it is a Mersenne number (power-of-2 - 1).

Weakness to OP's code: If RAND_MAX == pow(2,31)-1, a common occurrence, then OP's rand64() only returns values [0...pow(2,62)). @Nate Eldredge


Instead, loop as many times as needed.

To find how many random bits are returned with each call, we need the log2(RAND_MAX + 1). This fortunately is easy with an awesome macro from Is there any way to compute the width of an integer type at compile-time?

#include <stdlib.h>
/* Number of bits in inttype_MAX, or in any (1<<k)-1 where 0 <= k < 2040 */
#define IMAX_BITS(m) ((m)/((m)%255+1) / 255%255*8 + 7-86/((m)%255+12))
#define RAND_MAX_BITWIDTH (IMAX_BITS(RAND_MAX))

Example: rand_ul() returns a random value in the [0...ULONG_MAX] range, be unsigned long 32-bit, 64-bit, etc.

unsigned long rand_ul(void) {
  unsigned long r = 0;
  for (int i=0; i<IMAX_BITS(ULONG_MAX); i += RAND_MAX_BITWIDTH) {
    r <<= RAND_MAX_BITWIDTH;
    r |= rand();
  }
  return r;
}
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256