Search code examples
crandomcastingimplicit-conversionsrand

What happens behind the scenes if the parameter of srand function is an negative int?


The srand man says that srand has as parameter an unsignd int, but when using without casting the compile does not complain. Have any chance of going wrong if you do not use cast with (unsigned int) ? Or not makes difference, because the compiler always will convert it implicitly ?

As an example, a code below:

#include<stdio.h>
#include<stdlib.h>

int
main(void)
{
    int num, x;

    x = -2;

        srand(x);
    num = rand() % 100000;

    printf("%d\n", num);

    return 0;
}

The print of this code is:

32

I will assume that the compiler will implicitly convert the variable x of type int in unsigned int . What happens in this conversion ? What if the conversion convert the variable (that has type int) to an unsigned int that is the same value than an other number that when after the seed was generated, they will be identical. In other words:

int x = -2;
int y = 546; // the number 546 is just an example !

int num_x = srand(-2);
int num_y = srand(546);

And, behinde the scenes, num_x is equal to num_y.

This doubt appeared when I have had a negative parameter on my srand and I was in doubt if this seed would be unique. I think is not, because for an example, the code below:

#include<stdio.h>
#include<stdlib.h>

int
main(void)
{
    int t, num;
    t = -6;

    printf("%u\n", t);  // prints the variable t as unsigned (implicitly converted)
        srand(t);
    num = rand() % 100000;
    printf("Random number: %d\n", num);

    srand(4294967290);   // the number 4294967290 is how the int number -6 looks like when converted in unsigned int
    num = rand() % 100000;
    printf("New random number: %d\n", num);
    return 0;
}

The output of it:

4294967290
Random number: 19
New random number: 19

So, always negative numbers after converted will behave like this ? How do I take care of this ?


Solution

  • It will be implicitly converted, using -Wconversion with gcc and clang should provide a warning for this, clang gives the following warning:

    warning: implicit conversion changes signedness: 'int' to 'unsigned int' [-Wsign-conversion]
        srand(x);
        ~~~~~ ^
    

    the rules used for this conversion are covered in the draft C99 standard section 6.3.1.3 Signed and unsigned integers which says (emphasis mine):

    1. When a value with integer type is converted to another integer type other than _Bool, if the value can be represented by the new type, it is unchanged.

    2. Otherwise, if the new type is unsigned, the value is converted by repeatedly adding or subtracting one more than the maximum value that can be represented in the new type until the value is in the range of the new type.49)

    3. Otherwise, the new type is signed and the value cannot be represented in it; either the result is implementation-defined or an implementation-defined signal is raised.

    So for your first example -2 converted to unsigned int would be:

    UINT_MAX + 1 + -2
    

    which is UINT_MAX - 1, this leads to the useful property that -1 will always be converted to the max unsigned value for whatever unsigned type you are using.

    You can use limits.h to get UINT_MAX.

    The signed case is different and is covered by paragrah 3 above, it is implementation defined, which means the compiler decides how to deal with this case so you have to read the documentation if any provided to understand what will happen here. For example implementation defined behavior section for integers says for this case:

    • The result of, or the signal raised by, converting an integer to a signed integer type when the value cannot be represented in an object of that type (C90 6.2.1.2, C99 and C11 6.3.1.3).

      For conversion to a type of width N, the value is reduced modulo 2^N to be within range of the type; no signal is raised.

    So it treats the signed case similar to the unsigned case, the number just rolls over and it does not cause a signal to be raised. So for gcc this is well defined behavior but this is not portable behavior and so should not be relied on.