Saturn
Saturn

Reputation: 18149

Why is arc4random() behaving different when storing it in a variable or not?

    int chance = -5;
    int rand = arc4random() % 100;   // Number from 0 to 99
    if (rand <= chance) {            // This will never happen
        NSLog(@"This is... NOT POSSIBLE");
    }

Effectively, this never happens. But

    int chance = -5;
    if (arc4random() % 100 <= chance) {
        NSLog(@"This is... NOT POSSIBLE");
    }

Here, instead of storing it in a variable, I placed the random number expression directly in the condition. And the condition is fulfilled (sometimes).

Why is that? How can I debug this behavior?

Upvotes: 3

Views: 220

Answers (2)

Carl Norum
Carl Norum

Reputation: 224864

Type promotion rules.

arc4random returns an unsigned value. That means in your second case, the -5 gets promoted to that same unsigned type, turning it into 4294967291. 4+ billion is definitely larger than any number 0-99!

Let's walk through what happens in both of your examples.

  1. From your first example, in this line:

    int rand = arc4random() % 100;
    

    arc4random() returns an unsigned value. So then it looks like:

    int rand = someUnsignedNumber % 100;
    

    The 100 is a signed int, so it gets promoted to the same type as someUnsignedNumber, and the % operation is applied. After that you have:

    int rand = someUnsignedNumberBetween0And99;
    

    Assigning that unsigned number to int rand makes it back into a signed number. Your comparison then goes forward as expected.

  2. In the second example, you have this line:

    if (arc4random() % 100 <= chance)
    

    The same things happen with arc4random() % 100, yielding something like:

    if (someUnsignedNumberBetween0And99 <= chance)
    

    But here, chance is a signed number. It gets promoted, changing its value as described above, and you end up with the strange behaviour you're seeing.

Upvotes: 6

user529758
user529758

Reputation:

Silly, silly type system of C... If you read the man page for arc4random(), you find out that its prototype is

u_int32_t arc4random(void);

So it returns an unsigned integer.

When comparing its - unsigned - result with another integer, the unsignedness "wins": the other value (-5) is promoted to the unsigned type (u_int32_t in this case), it rolls over (since unsigned integer "underflow" is designed to work like this in C - you'll get 2 ^ 32 - 5) and so an "erroneous" (i. e. behaving-as-unexpected) comparison occurs.

When you explicitly assign the value to an int (i. e. signed) variable, this promotion does not occur since the comparison is between two signed types, so it is evaluated as you would expect.

Upvotes: 5

Related Questions