Search code examples
clinuxwindowsrandomrandom-seed

Why does the c function srand() behave differently on Windows vs. Linux, where consecutive seeds produce the first consecutive random number?


Here we have a piece of c code that goes into a dead loop, reading the current timestamp and resetting the random seed each time, after which it prints a random number and sleeps for one second. The seeds are consecutive, and Windows generates the first random number consecutively for consecutive seeds, which is not the case with Linux.

Many languages written in c that don't have their own implementation of a random number generator also have this problem, e.g. lua 5.1

#include<time.h>
#include<stdlib.h>
#include<stdio.h>
#ifdef __linux__
#include <unistd.h>
#else
#include <windows.h>
#endif

void printRandoms(int lower, int upper,
                            int count)
{
    int i;
    for (i = 0; i < count; i++) {
        int num = (rand() %
        (upper - lower + 1)) + lower;
        printf("%d\n", num);
    }
}

int main(){
    while(1){
        srand(time(NULL));
        printRandoms(1,10000,1);
        #ifdef __linux__
        sleep(1);
        #else
        Sleep(1000);
        #endif
    }
    return 0;
}

The output:

On Windows:
I tried both gcc and clang.
1. gcc.exe (MinGW-W64 x86_64-ucrt-posix-seh, built by Brecht Sanders) 13.1.0
2. (built by Brecht Sanders) clang version 16.0.5
3. gcc.exe (MinGW-W64 x86_64-msvcrt-posix-seh, built by Brecht Sanders) 9.5.0
5237
5238
5239
5240
5241
5242
5243
5245
5246
5247
5248

On Linux:
gcc (Ubuntu 9.4.0-1ubuntu1~20.04.1) 9.4.0
5142
3660
7270
896
9500
3041
6659
5194
8809
7361
5960

I know that's usually the wrong way to use srand(), but was just curious what led to the difference.


Solution

  • You seem to think time( NULL ) is a strong seed, but it's not. While the Linux version looks more random, it's not. It's just as predictable as the Windows version. That's because it's often possible to guess them time of seeding. And knowing that gives the stream of numbers, in both versions. (Even if you don't know the exact time, you just have a few possibilities of streams.) srand should be given a value as random as possible.


    The Windows version appears to return the seed as its first value. After all, you should have provided a random value to srand. The Linux one skips returning the seed and starts with the value that follows the seed. As such, you can replicate the behaviour of the Linux rand by calling rand after calling srand.

    srand(time(NULL));
    (void)rand();
    printRandoms(1,10000,1);
    

    This will make the Windows code look just as unpredictable as the Linux version looks.


    Note that it doesn't make sense to call srand with a random seed multiple times in a row.

    Note that rand should not be used anywhere where randomness is important (e.g. crpytography). Use a proper source of randomness for that.