Code for MS rand() and srand() in VC++ 6.0

My friend created a registration code generator for his software on Windows, and he wanted to transplant it into a web app.

He asked for my help. I think it seems really easy, so I agreed without hesitation. Then a long story began...

The different results on Windows and Linux

As we know, srand will set the current number stored in the memory which is known as seed, and rand will use the seed to calculate and return a new number.

The source code of the registration code generator looks like this:

for(int i=0; i<5; i++) {    
    srand(seed[i]);
    strKey.Format(strKey+_T("%c"), _T('A')+rand()%26);
}

The seed in the code above is generated from the user's machine id. Now it seems evident that we combine the usages of rand of srand to generate a static registration code.

I use cgo to wrap the origin C++ code, however, the result is not as expected. After some debugging, I found the executing result of rand on Linux is different from the one on Windows.

Digging the source code

The source code of Linux rand is easy to find:

/* If we are using the trivial TYPE_0 R.N.G., just do the old linear
   congruential bit.  Otherwise, we do our fancy trinomial stuff, which is the
   same in all the other cases due to all the global variables that have been
   set up.  The basic operation is to add the number at the rear pointer into
   the one at the front pointer.  Then both pointers are advanced to the next
   location cyclically in the table.  The value returned is the sum generated,
   reduced to 31 bits by throwing away the "least random" low bit.
   Note: The code takes advantage of the fact that both the front and
   rear pointers can't wrap on the same call by not testing the rear
   pointer if the front one has wrapped.  Returns a 31-bit random number.  */

int
__random_r (buf, result)
     struct random_data *buf;
     int32_t *result;
{
  int32_t *state;

  if (buf == NULL || result == NULL)
    goto fail;

  state = buf->state;

  if (buf->rand_type == TYPE_0)
    {
      int32_t val = state[0];
      val = ((state[0] * 1103515245) + 12345) & 0x7fffffff;
      state[0] = val;
      *result = val;
    }
  else
    {
      int32_t *fptr = buf->fptr;
      int32_t *rptr = buf->rptr;
      int32_t *end_ptr = buf->end_ptr;
      int32_t val;

      val = *fptr += *rptr;
      /* Chucking least random bit.  */
      *result = (val >> 1) & 0x7fffffff;
      ++fptr;
      if (fptr >= end_ptr)
    {
      fptr = state;
      ++rptr;
    }
      else
    {
      ++rptr;
      if (rptr >= end_ptr)
        rptr = state;
    }
      buf->fptr = fptr;
      buf->rptr = rptr;
    }
  return 0;

 fail:
  __set_errno (EINVAL);
  return -1;
}

The source code of Windows rand really took me a lot of time to discover the truth, and here let's skip my three-hour search and experimentation. The source code is finally found in an ancient discussion in a forum which is posted in 2003...

Here is the source code of rand in VC++ 6.0(or still the same as the one today in Visual Studio 2022...):

static long holdrand = 1L;
int __cdecl rand (void)
{
    return(((holdrand = holdrand * 214013L + 2531011L)>>16) & 0x7fff);
}
void __cdecl srand (unsigned int seed)
{
    holdrand = (long)seed;
}

and the functions are finally implemented into Typescript:

class WinRandom {
  next: number;
  rand(this: WinRandom): number {
    this.next = this.next * 214013 + 2531011;
    return (this.next >> 16) & 0x7fff;
  }
  srand(this: WinRandom, seed: number) {
    this.next = seed;
  }
}

Reference

Last updated