cocoa - IF failing for double? - iphone

I have a set of NSTimeIntervals like this:
NSArray *mySet = [NSArray arrayWithObjects:
[NSNumber numberWithDouble: time1], //
[NSNumber numberWithDouble: time2],
[NSNumber numberWithDouble: time3],
nil];
// suppose that at this time
// time1 = 0.00000
// time2 = 18.3200
// time3 = 21.6500
at some point in my code I want to test if currentTime is greater or equal to one of the times on the array, so I do
// currentTime = 18.32 right now
for (int i=0; i<[mySet count]; i++) {
if (currentTime >= [[mySet objectAtIndex:i] doubleValue]) {
NSLog(#"%d...", i);
}
}
The output should be "1...2..."
but the output is just "1..."
when i = 1,
it is comparing 18.32 to 18.32 and failing to recognize that the value is equal or greater than the other? WTF???
How can that be?
thanks for any help.

Typically when comparing floating point values you want to decide how close one needs to be to the other (an epsilon or error bound), then just check if they are within that. The easiest way to do that is subtract one from the other then check if the absolute value of the result is less than or equal to your epsilon.
Pseudocode:
var values = [...];
var toCheck = ...;
var epsilon = 0.00001;
for (var i = 0; i < length(values); ++i)
if (abs(values[i] - toCheck) <= epsilon)
print "they are close enough"
end
end

If you're trying to do precise calculations, you shouldn't use NSNumber but NSDecimalNumber. The former is just a wrapper for storing numbers as objects whereas the second is designed to preserve precision when doing calculations. It works better than scalars at extreme ranges.
See Numbers and Values Programming Topics for Cocoa

You can't compare double values for equality except for certain cases. (1.00000 == 1.0000) would probably ring true, but (1.3000 == 1.3000) would most likely not. This is because doubles aren't actually decimal numbers; and not all numbers can be represented by a double; your values are likely something like 18.320000123 and 18.320000473; that is to say; there's probably a lot of trailing garbage that is uninteresting at the precision you have chosen, but none the less keep them from comparing as equal.
It doesn't help that you run one of them through NSNumber; twice.

You declare var i as an int and then try to nslog it at as double value without a cast. The log might be failing independent of the precision of your test.

Related

Creating and managing two independent random number sequences

I'm having trouble generating two independent random sequences using the rand and srand functions. The details are below, any help would be most appreciated.
I'm working on a puzzle game for the iPhone, and usually for the random number generation I use the arc4 function. However for the multiplayer mode I want both players to have the same pieces throughout the game, and the only way I can control it is to have two repeatable random sequences. If I then send the seeds to the other device, the games will be identical. However when I use rand and srand and try to switch to the other seed, the sequence starts from scratch, I somehow have to initialize two independent sequences generated with a seed.
Thank you for your responses
Cryptographically bad PRNGs like rand() operate by feeding the previous result back into a certain mathematical procedure.
In order to continue a sequence from where it left off, all you have to do is store the last-generated number and use it as the seed:
srand(time(0));
int player1_rand_num = rand();
NSLog(#"Player 1: %d, %d, %d", rand(), rand(), rand());
srand(7);
int player2_rand_num = rand();
NSLog(#"Player 2: %d, %d, %d", rand(), rand(), rand());
// Re-seed Player 1 sequence
srand(player1_rand_num);
// Displays the same "random" numbers as the first NSLog
NSLog(#"Player 1 again: %ld, %ld, %ld", rand(), rand(), rand());
// and so on...
The random() function generates better random numbers, and has a separate pair of functions, initstate() and setstate() which will give you the state of the generator. You can store the state and pass it into setstate() to resume the sequence from where you left off. I direct you to man 3 random for the details.
First off, as others have pointed out already, you should use random() instead of rand(). Secondly, while your singleton approach may work for you, you could solve your problem more easily and IMHO more elgantly by using setstate(3). See Use of setstate(3) doesn't produce expected sequence of random numbers for an example on how to switch between two random number states.
Thank you for the suggestions, this is how I implemented the whole thing. I created a singleton class with 2 instance variables - seed1 and seed2 - anytime I want to get a number from the first generator I use the method generator1, same for generator2 method. The seed1/2 is instantly set to a newly generated number every time so I can just continue where I left off. In conlusion, Josh Caswell gave me all the information I needed. Check out the code if you ever need something like this. The object inits with seeds 1 and 1 but during the game they get replaced with some other numbers that both devices share.
#implementation RandomNumberGenerator
#synthesize seed1,seed2;
static RandomNumberGenerator *sharedGenerator = nil;
+(RandomNumberGenerator *) sharedInstance
{
if(!sharedGenerator) {
sharedGenerator = [[RandomNumberGenerator alloc] initWithSeed1:1 andSeed2:1];
}
return sharedGenerator;
}
-(id) initWithSeed1:(int) seedOne andSeed2:(int) seedTwo{
self = [super init];
if (self)
{
seed1 = seedOne;
seed2 = seedTwo;
}
return self;
}
-(int) generator1{
srand(seed1);
int j = rand();
seed1 = j;
return abs(j);
}
-(int) generator2 {
srand(seed2);
int k = rand();
seed2 = k;
return abs(k);
}
-(int) giveRandom {
//return abs(arc4random());
return abs(arc4random());
}
#end
Did you seed your random number generator?
srand( myIdenticalSeedValueForBothPartners );
See this question or here [C++ reference].
In case you don't need to call rand() many thousand times:
int nthRandBasedOnSeed( int seed, int count ) {
srand( seed );
int result;
while( 0 < count-- ) {
result = rand();
}
return result;
}
Alternately, you might consider sending with the seed a "count". This count would simply indicate where in the seeded-series you are and would get incretented each time you generate a random number with that seed. This approach gives you the flexibility of using any random generator you like and keeps communication to a minimum.
int playerSeed = 12345;
int playerRndCount = 0;
int generateRandomNumber() {
playerRndCount++;
return rand();
}
void synchSeed(seed, count) {
srand(seed);
for (int i=0; i<count; i++)
generateRandumNumber();
}
Some random number generator libraries allow you to save the state of the generator. This way, you can restore it later, and continue with a sequence already in progress. One I know of is called RandomLib, and it can be found on SourceForge.
Another option is save the seed, and count how many times you've pulled a value from the generator after seeding. Later on when you want to continue, reseed with the original seed, and pull off the same quantity. This probably isn't the best method, but should work fine if not done a lot.

doubleValue does not convert the object to a double value correctly at all times

I have an NSArray in which I would like to store double values. I defined it as follow
NSMutableArray *setOfDoubles;
I add the elements as follow
NSNumber *num;
num = [NSNumber numberWithDouble:someDouble];
[setOfDoubles addObject:num];
And I read the element as follow
num = [setOfDoubles lastObject];
[setOfDoubles removeLastObject];
doubleValueToUse = [num doubleValue];
My problem is sometimes (Not always), for example when num (as an object) is 5.1, doubleValueToUse (as a double value) is 5.099999999999996. The way I figured num (as an object) is 5.1 is that I debug and when I am hovering the mouse on top of num on the line num = [setOfDoubles lastObject]; it shows 5.1 but after doubleValue conversion it becomes the number I mentioned. Does anybody know why is this happening?
Not every number can be accurately represented using a float variable. For example, you can't precisely represent, say, 1/3 using a finite number of digits in our common decimal (base-10) system, but in ternary (base-3), it would be just 0.1. Similarly, the numbers you can write with a finite number of digits in decimal, may not necessarily have the finite number of digits in their binary representation, hence the error.
A few links on the topic if you are interested:
http://floating-point-gui.de/basic/
http://www.mathworks.com/support/tech-notes/1100/1108.html
http://download.oracle.com/docs/cd/E19957-01/806-3568/ncg_goldberg.html
This is normal for float values.
If you want to save initial (same) representation of float numbers in all places of your code, you can save them, for example, in NSString. When you will need float number you will just write [NSString floatValue];. But it is not effective if you have large amount of float values.

NSNumber LoopVar++ in for loop increments by 4 instead of by 1

I'm quite sure that this question has a very simple answer that I should have figured out by now. Since I haven't yet done so I come to you, stack overflow hive mind.
I expected the loop below to print out the numbers from 0 to 5. Instead it prints out only 0 and 4. Why does LoopNumber++ increment my NSNumber LoopNumber by 4 instead of by 1?
NSNumber *LoopNumber;
for (LoopNumber=0; LoopNumber<=5; LoopNumber++) {
NSLog(#"%d",LoopNumber);
}
If I change it to the following it works exactly as I expect. What gives?
for (int LoopNumber=0; LoopNumber<=5; LoopNumber++) {
I'm fooling around with an iPhone project in XCode 3.2.1, using SDK 3.1.2.
an NSNumber is not an integer. It is an object wrapper for a number which may be an integer.
The variable LoopNumber is actually a pointer to the location in memory where the object should be. All LoopNumber itself holds is a memory address, which on your machine is 4 bytes long. When you do LoopNumber++ you are inzoking pointer aritmatic on the pointer and it is advancing to the next memory address which is four bytes later. You can see this by doing a sizeof(LoopNumber) - that would return 4 on your system.
What you really want to do is use a regular integer like so:
NSUInteger loopNumber;
for(loopNumber = 0; loopNumber <= 5; loopNumber++) {
NSLog(#"%d",loopnumber);
}
or if you really need to use NSNumbers:
NSNumber loopNumber;
for(loopNumber = [NSNumber numberWithInt:0]; [loopNumber intValue] <= 5; loopNumber=[NSNumber numberWithInt:[loopNumber intValue]++]) {
NSLog(#"%d",loopnumber);
}
int is native type. NSNumber is a Objective-C class. Use float or int when doing real work. But to put a int into a collection you can create an NSNumber object from the int or float native type.

Incorrect value for sum of two NSIntegers

I'm sure I'm missing something and the answer is very simple, but I can't seem to understand why this is happening. I'm trying to make an average of dates:
NSInteger runningSum =0;
NSInteger count=0;
for (EventoData *event in self.events) {
NSDate *dateFromString = [[NSDate alloc] init];
if (event.date != nil) {
dateFromString = [dateFormatter dateFromString:event.date];
runningSum += (NSInteger)[dateFromString timeIntervalSince1970];
count += 1;
}
}
if (count>0) {
NSLog(#"average is: %#",[NSDate dateWithTimeIntervalSince1970:(NSInteger)((CGFloat)runningAverage/count)]);
}
Everything seems to work OK, except for runningSum += (NSInteger)[dateFromString timeIntervalSince1970], which gives an incorrect result. If I put a breakpoint when taking the average of two equal dates (2009-10-10, for example, which is a timeInterval of 1255125600), runningSum is -1784716096, instead of the expected 2510251200.
I've tried using NSNumber and I get the same result. Can anybody point me in the right direction?
Thanks!
Antonio
Is there some reason you are fudging about with NSInteger?
[NSDate timeIntervalSince1970] returns an NSTimeInterval, which is basically a double. Clean up the code and see if that helps.
NSInteger can hold values up to the INT_MAX limit that equals 2147483647 - so your value overflows the integer types limit - remember that timeInterval is a double type.
You can try to use double type in all your calculations or use -timeIntervalSinceReferenceDate method - it returns interval since 1 January 2001 and you might avoid overflow as well.
If your events object is an array or other type that allows to get its size then you can add time interval values already divided by count - that also may help to avoid overflow:
NSTimeInterval runningSum = 0;
NSInteger count = [self.events count];
for (...){
...
runningSum += [dateFromString timeIntervalSince1970]/count;
}
NSInteger is a 32 bit signed integer on iPhone and is therefore limited to values between −2147483648 and +2147483647.
You may get the desired result by using NSUInteger which is an unsigned 32 bit integer able to contain values between 0 and +4294967295.
You should however pay attention to the number of runs through the loop so you don't wrap the values.

Do I need to use decimal places when using floats? Is the "f" suffix necessary?

I've seen several examples in books and around the web where they sometimes use decimal places when declaring float values even if they are whole numbers, and sometimes using an "f" suffix. Is this necessary?
For example:
[UIColor colorWithRed:0.8 green:0.914 blue:0.9 alpha:1.00];
How is this different from:
[UIColor colorWithRed:0.8f green:0.914f blue:0.9f alpha:1.00f];
Does the trailing "f" mean anything special?
Getting rid of the trailing zeros for the alpha value works too, so it becomes:
[UIColor colorWithRed:0.8 green:0.914 blue:0.9 alpha:1];
So are the decimal zeros just there to remind myself and others that the value is a float?
Just one of those things that has puzzled me so any clarification is welcome :)
Decimal literals are treated as double by default. Using 1.0f tells the compiler to use a float (which is smaller than double) instead. In most cases it doesn't really matter if a number is a double or a float, the compiler will make sure you get the right format for the job in the end. In high-performance code you may want to be explicit, but I'd suggest benchmarking it yourself.
As John said numbers with a decimal place default to double. TomTom is wrong.
I was curious to know if the compiler would just optimize the double to a const float (which I assumed would happen)... turns out it doesn't and the idea of the speed increase is actually legit... depending on how much you use it. In math-heavy application, you probably do want to use this trick.
It must be the case that it is taking the stored float variable, casting it to a double, performing the math against the double (the number without the f), then casting it back to a float to store it again. That would explain the diference in calculation even though we're storing in floats each time.
The code & raw results:
https://gist.github.com/1880400
Pulled out relevant benchmark on an iPad 1 in Debug profile (Release resulted in even more of a performance increase by using the f notation):
------------ 10000000 total loops
timeWithDoubles: 1.33593 sec
timeWithFloats: 0.80924 sec
Float speed up: 1.65x
Difference in calculation: -0.000038
Code:
int main (int argc, const char * argv[]) {
for (unsigned int magnitude = 100; magnitude < INT_MAX; magnitude *= 10) {
runTest(magnitude);
}
return 0;
}
void runTest(int numIterations) {
NSTimeInterval startTime = CFAbsoluteTimeGetCurrent();
float d = 1.2f;
for (int i = 0; i < numIterations; i++) {
d += 1.8368383;
d *= 0.976;
}
NSTimeInterval timeWithDoubles = CFAbsoluteTimeGetCurrent() - startTime;
startTime = CFAbsoluteTimeGetCurrent();
float f = 1.2f;
for (int i = 0; i < numIterations; i++) {
f += 1.8368383f;
f *= 0.976f;
}
NSTimeInterval timeWithFloats = CFAbsoluteTimeGetCurrent() - startTime;
printf("\n------------ %d total loops\n", numIterations);
printf("timeWithDoubles: %2.5f sec\n", timeWithDoubles);
printf("timeWithFloats: %2.5f sec\n", timeWithFloats);
printf("Float speed up: %2.2fx\n", timeWithDoubles / timeWithFloats);
printf("Difference in calculation: %f\n", d - f);
}
Trailing f: this is a float.
Trailing f + "." - redundant.
That simple.
8f is 8 as a float.
8.0 is 8 as a float.
8 is 8 as integer.
8.0f is 8 as a float.
Mostly the "f" can be style - to make sure it is a float, not a double.