Reputation: 1101
I'm trying to get a random number using arc4random between -60 and 60. I generate avnumber between 1 and 120 inclusively, and then subtract 60.
If this is in one line, I get weird results whenever the number is negative.
//Loop illustrates point better.
while (1) {
//Gets garbage numbers approximately half the time (when random1 should be negative)
NSInteger random1 = 1 + (arc4random() %120) - 60;
NSLog (@"random1: %li", random1);
//Essentially same thing, but works fine.
NSInteger random2 = 1 + (arc4random() %120);
NSInteger random3 = random2 - 60;
NSLog (@"random2: %li random3: %li", random2, random3);
}
Why does this happen?
Upvotes: 3
Views: 930
Reputation: 7710
arc4random()
returns an unsigned integer, 1 + (arc4random() %120) - 60;
is interpreted as unsigned, then assigned to NSInteger random1
.
You can either cast the return value of (arc4random() %120)
to a signed integer:
NSInteger random1 = 1 + (NSInteger)(arc4random() %120) - 60;
or store the intermediate result of arc4random()
in an unsigned integer:
NSInteger r = (arc4random() %120);
NSInteger random1 = 1 + r - 60;
Also note that the arc4random man page recommends using arc4random_uniform()
over modulo constructs:
arc4random_uniform() is recommended over constructions like ``arc4random() % upper_bound'' as it avoids "modulo bias" when the upper bound is not a power of two.
Upvotes: 1
Reputation: 437482
I suspect the problem stems from the fact that arc4random
is returning a u_int32_t
(an unsigned 32-bit integer) between 0 and (2**32)-1, but doing a calculation only appropriate for signed integers.
Regardless, when generating random numbers, it's better to use arc4random_uniform
, which avoids the modulo bias of your code. This bias will be barely observable, but nonetheless, you should use:
NSInteger random = 1 + (NSInteger)arc4random_uniform(120) - 60;
Or, technically, if you want numbers between -60 and 60 inclusively, you should use:
NSInteger random = (NSInteger)arc4random_uniform(121) - 60;
Upvotes: 3
Reputation: 112857
For starters arc4random()
returns an u_int32_t
, unsigned 32 bit number. Unsigned numbers can not be negative so subtracting a number larger then the unsigned value results in unspecified behavior.
In the second case you assign the mod'ed value to a signed integer that can have a negative number.
As @Rob states in his answer, use arc4random_uniform
instead of mod'ing to get a range in order to eliminate bias.
Upvotes: 0