I'm trying to put some objects in the screen at random positions.
I use arc4random() to generate a new random number.
But it seems that the function is not working properly, here's the code and traced result:
Code :
UIView *stateView = [[UIView alloc] initWithFrame:CGRectMake( (arc4random()%700)-100 , (20 * 91) + 378 + ((arc4random()%600)+200), 325 , 188)];
NSLog(#"Note %d : X = %f , Y = %f",i,stateView.frame.origin.x,stateView.frame.origin.y);
**********************
NSLog Output :
Note 5 : X = 4294967040.000000 , Y = 2552.000000
Is this a bug or I'm doing wrong with the generator ?
According to the documentation for arc4random(), the return type is u_int32_t, which is an unsigned type. In the expression
(arc4random()%700)-100
the calculation, including the subtraction, is done using unsigned arithmetic. You are getting unsigned arithmetic overflow. To fix this, cast to a signed integer type before doing the subtraction:
((int) (arc4random() % 700)) - 100
The result of this will be an integer from -100 through 599.
Related
Sometime arc4random() gives negative number also in objective C.
My code is as follow:
Try 1:
long ii = arc4random();
Try 2:
int i = arc4random();
How can I only get positivite random number?
Thank you,
No, it's always positive as it returns an unsigned 32-bit integer (manpage):
u_int32_t arc4random(void);
You are treating it as a signed integer, which is incorrect.
You should use the arc4random_uniform() function. this is the most common random function used.
arc4random_uniform() function
Returns a random number between 0 and the inserted parameter minus 1.
For example arc4random_uniform(3) may return 0, 1 or 2 but not 3.
Example
u_int32_t randomPositiveNo = arc4random_uniform(5) + 1; //to get the range 1 - 5
I'm using the Xcode debugger, and these numbers don't seem to add. I was curious as to why :
(lldb) p height
(CGFloat) $R0 = 2.1815627849240522E-314
(lldb) p frame.size
(CGSize) $R1 = (width = 375, height = 1000)
(lldb) p frame.size.height
(CGFloat) $R2 = 1000
(lldb) p height + frame.size.height
(CGFloat) $R3 = 1000
I have 3 questions about this..
What is 2.1815627849240522E-314 ? What kind of number is that?
Is it possible to instantiate temporary variables in debugger like you would in Ruby console, Chrome console etc.? For example, let temp_x = 4 ?
Why does my $R3 return 1000 and not.. something bigger?
2.1815627849240522E-314 is essentially zero. 1000 + 0 = 1000.
Floating point numbers have two parts - mantissa and exponent (position of the decimal point). e-341 means "move the decimal point 341 places to the right"
Adding 1000 to that very very small number creates a number that would be equal
1000.000...(> 300 zeros here)...2181567849240522
Even double mantissa can't represent numbers like this (double can represent up to 17 decimal digits) so it takes only the more important numbers in the beginning.
I encourage you to read through What Every Programmer Should Know About Floating-Point Arithmetic
Why does my method return values < 0.4 in some cases?
e.g. 0.225501
#define ARC4RANDOM_MAX 0x100000000
float myVar = [self randomFloat:0.4 to:2];
- (float)randomFloat:(int)from to:(int)to
{
return ((double)arc4random() / ARC4RANDOM_MAX) * (to - from) + from;
}
You are casting your parameters to integers (which in your case changes your range to between 0 and 2), change the parameters to be float.
- (float)randomFloat:(float)from to:(float)to
when dividing and using floats the precision of the decimals is sometimes lost. Maybe you can use a long with N fixed number of digits and place the decimal point before those digits. The other day I was getting strange results when adding (1 + (3/10))= should be 1.3 but I always had something like 1.29995 . Hope it helps
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?
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.
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.
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.
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.
Can I set a range of numbers when using arc4random()? For example 50-100 only.
As pointed out in other posts below, it is better to use arc4random_uniform. (When this answer was originally written, arc4random_uniform was not available). Besides avoiding the modulo bias of arc4random() % x, it also avoids a seeding problem with arc4random when used recursively in short timeframes.
arc4random_uniform(4)
will generate 0, 1, 2 or 3. Thus you could use:
arc4random_uniform(51)
and merely add 50 to the result to get a range between 50 & 100 (inclusive).
To expand upon JohnK comment.
It is suggested that you use the following function to return a ranged random number:
arc4random_uniform(51)
which will return a random number in the range 0 to 50.
Then you can add your lower bounds to this like:
arc4random_uniform(51) + 50
which will return a random number in the range 50 to 100.
The reason we use arc4random_uniform(51) over arc4random() % 51 is to avoid the modulo bias. This is highlighted in the man page as follows:
arc4random_uniform(upper_bound) will return a uniformly distributed random number less than upper_bound. 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.
In short you get a more evenly distributed random number generated.
int fromNumber = 10;
int toNumber = 30;
int randomNumber = (arc4random()%(toNumber-fromNumber))+fromNumber;
Will generate randon number between 10 and 30, i.e. 11,12,13,14......29
You can use this code for generating random values with range:
//range from 50 to 100
int num1 = (arc4random() % 50) + 50; or
int num1 = arc4random_uniform(50) + 50;
//range from 0-100
int num1 = arc4random() % 100; or
int num1 = arc4random_uniform(100);
In Swift you can use this (inspired by answer of #Justyn)
func generateRandomKey(fromRange rangeFrom:Int, toRange rangeTo:Int) -> Int{
let theKey = arc4random_uniform(UInt32(rangeTo - rangeFrom)) + UInt32(rangeFrom)
return Int(theKey)
}
Will always give you a random range Integer.
In many situations 10 thru 30 would mean inclusive, (includes 10 and 30) ...
int fromNumber = 10;
int toNumber = 30;
toNumber ++;
int randomNumber = (arc4random()%(toNumber-fromNumber))+fromNumber;
Notice the difference toNumber - fromNumber is now 21 ... (20+1) which yields the possible results of 0 thru 20 (inclusive) which when added to fromNumber (10) results in 10 thru 30 (inclusive).