Reputation: 4388
I am trying to compare two integers. One is the row
of an NSIndexPath
, the other is the count
of an NSArray
. The row
is equal to 0 and the count
is equal to 2. I have them in an if
statement as follows:
if([self.selectedSetpoint row] < ([self.theCategories count]-3))
{
//Do true stuff
}
So by my math, the if
statement should be false as I am comparing 0 < -1
. However, I keep getting this statement coming through as true, and the block inside the if
is being run. I have tried to NSLog
the values to make sure that I am not just getting the wrong value. I placed this right BEFORE the if
statement:
NSLog(@"%d",[self.selectedSetpoint row]);
NSLog(@"%d",[self.theCategories count]-3);
NSLog(@"%@",[self.selectedSetpoint row] < ([self.theCategories count]-3)?@"Yes":@"No");
and got this on the console:
2012-07-17 08:58:46.061 App[61345:11603] 0
2012-07-17 08:58:46.061 App[61345:11603] -1
2012-07-17 08:58:46.062 App[61345:11603] Yes
Any ideas why this comparison is coming up as true? Am I misunderstanding something about comparing integers?
I have another if statement just above this one that compares
[self.selectedSetpoint row]<([self.theCategories count]-2)
Which is 0 < 0
, and it works fine (returns NO
). So I feel like there is some issue with the use of negative integers that I am not getting.
Thank you in advance.
Upvotes: 3
Views: 2584
Reputation: 6058
I was going to propose an alternative solution - namely to avoid using subtraction and instead use addition on the other side of the equation:
if ([self.selectedSetpoint row] + 3 < [self.theCategories count])
{
//Do true stuff
}
This sidesteps getting caught by these kind of underflow bugs, however it leaves another gotcha untouched... Namely the conversion rules referred to in the answer to this question: What are the general rules for comparing different data types in C?
Quoting from an answer to that question, you see the C99 spec states:
So if you have a negative value for [self.selectedSetpoint row] + 3
then the comparison will fail...
The other answers here have advocated casting (NSUInteger) to (NSInteger) - but note that this may cause overflow problems if your unsigned values are significantly massive. For e.g:
(NSInteger) -3 < (NSInteger) 4294967289 == false...
Figuring there must be an easy way to solve this, I first came up with a hard way to solve it...
#define SafeLT(X, Y) \
({ typeof (X) _X = (X); \
typeof (Y) _Y = (Y); \
( _X < (NSInteger) 0 ? (( _Y > 0) ? YES : _X < _Y ) : ( _Y < (NSInteger) 0 ? NO : _X < _Y));})
This should work regardless of how you mix NSUInteger and NSInteger, and will ensure that the operands are evaluated at most once (for efficiency).
By way of proof of correctness:
_X < (NSInteger) 0
to evaluate to True, _X must be NSInteger and < 0, so we check if _Y > 0. The compiler will make the correct comparison here by evaluating the type of _Y. If Y > 0 then by definition we return YES. Otherwise we know both X and Y are signed and < 0 and can compare them safely.A simpler solution, however, would be to just typecast to a longer signed integer (iff your system has one):
#define EasySafeLT(X, Y) \
({ long long _X = (X); \
long long _Y = (Y); \
( _X < _Y);})
Although this depends on having the larger type available so may not always be feasible.
Upvotes: 2
Reputation: 24041
the problem is the count
property is kind of NSUInteger
and when you subtract a higher number from a smaller you won't get a less than zero number, you will get a very large positive number, it cause the strange behaviour.
try this way, and you would get the good result:
NSLog(@"%@",(NSInteger)[self.selectedSetpoint row] < ((NSInteger)[self.theCategories count]-3)?@"Yes":@"No");
Upvotes: 0
Reputation: 32066
This does appear to be a case of comparing signed ints to unsigned ints. Your log statements will throw your assumptions because you are asking for the numbers to be printed signed.
NSLog(@"%d", -1); //Signed
-1;
NSLog(@"%u", -1); //Unsigned (tested on ios, 32 bit ints)
4294967295;
And then when you compare: 0 < 4294967295
certainly is true.
Casting as @ctrahey suggests should fix your problem:
if([self.selectedSetpoint row] < ( (int)[self.theCategories count] -3 ))
{
//Do true stuff
}
Upvotes: 0
Reputation: 18290
I suspect the issue is that the return of count
is an unsigned integer, and subtracting more than it's magnitude, it underflows and becomes quite large. I have run some tests, and I get the same basic behavior as you (it looks like it's -1
, and in certain contexts it appears to be working as expected... however it is clearly being underflowed in the context of the if()
block.
Silly problem, but luckily there is a simple solution: Cast it in place in the if statement:
if([self.selectedSetpoint row] < ( (int)[self.theCategories count] -3 ))
{
//Do true stuff
}
Upvotes: 7