Objective C: Unsigned int compare - objective-c

So I ran into a huge issue at work because I had something like this in my code:
int foo = -1;
NSArray *bar = [[NSArray alloc] initWithObjects:#"1",#"2",#"3", nil];
if (foo > [bar count]){
NSLog(#"Wow, that's messed up.");
} else {
NSLog(#"Rock on!");
}
As you probably already know by me posting this, the output is:
"Wow, that's messed up."
From what I gather, objective C is converting my negative number to a "signed" int and thus, killing my compare.
I saw other posts about this and they all stated what the problem was but none of them suggested any simple solutions to get this comparison to actually work. Also, I'm shocked that there are no compiler warnings, as these are causing serious issues for me.

The problem
The problem you're experiencing is that because foo is a signed integer and -[NSArray count] returns an unsigned integer, foo is undergoing implicit type conversion to unsigned integer. See Implicit Type Conversion for more information. Also, there's more information about type conversion rules in C here.
The solution
-[NSArray count] returns an unsigned value because an array can never have a negative number of elements; the smallest possible value is 0. Comparing an array's count to -1 doesn't make a lot of sense -- the count will always be larger than any negative number (sign problems notwithstanding).
So, the right solution here, and the way to avoid these kinds of problems, is to use a type that matches the return value of -[NSArray count], namely NSUInteger (the U is for unsigned).

Try this
- (IBAction)btnDoSomething:(id)sender {
int foo = -1;
NSArray *bar = [[NSArray alloc] initWithObjects:#"1",#"2",#"3", nil];
if ( foo > (signed)[bar count] ) {
NSLog(#"Wow, that's messed up.");
} else {
NSLog(#"Rock on!");
}
}
Working
If you are comparing two different type of variables then it will implicitly convert data type of both variables to higher type of them.
In this example,
variable foo is of type signed int and array count is of unsigned int,
So it will convert data type of foo to unsigned int
then value of foo will become large number, which is smaller than array count 3.
So in this example you need to down cast array count to signed int.
Issues
When your array count exceeds max limit of signed int then after casting it will rounded back like [ -(negative) max limit -> 0 -> + max limit ] which is unexpected result.
Solution
Avoid type casting if you are not sure about maximum array length.
Do casting if you are sure for limit(maximum array length will not exceeds signed int max limit).
For more details check this
http://visualcplus.blogspot.in/2006/02/lesson-4-casting-data-types.html

Related

NSUInteger casting

I have the following statement:
NSLog(#"The count of the book is now: %i", [book.book count]);
This gives me a caution that I'm using the wrong format argument.
1) Why can't I print the count as an integer? (Wouldn't this make the most sense?)
2) What would be the correct way to write the above statement?
You can, but probably shouldn't print it as a signed integer, because it may contain values that would overflow the signed integer. Additionally, NSUInteger is either defined as unsigned int, or unsigned long depending on wether the platform is 32 or 64 bit, so you can handle this by casting to unsigned long and then using the unsigned long format specifier.
NSLog(#"The count of the book is now: %lu", (unsigned long)[book.book count]);

finding the closest Int value from a set of int values in objective c

objective c math function question
I've got a x value that i'd like to compare to other values within a set, then determine which value from the set my x value is closest to.
For example, lets say i've got the ints 5, 10, 15, 20, 25.
What is the best way to determine which of these numbers is closest to 7?
int closestDistance = INT32_MAX;
int indexOfClosestDistance = -1;
int x = 7;
for (int i=0; i < [yourArray count]; i++)
{
int num = yourArray[i];
int diff = abs(num - x);
if (diff < closestDistance)
{
closestDistance = diff;
indexOfClosestDistance = i ;
}
}
Best of luck
Neither Objective-C nor Cocoa provides anything that solves this for you. You can store your ints in a plain old array of int, or you can wrap each one in an NSNumber and store the wrappers in an NSArray.
If you're going to probe the array many times, sort it once in advance, and then for each probe use a binary search (standard C function bsearch or Core Foundation's CFArrayBSearchValues or Cocoa's -[NSArray indexOfObject:inSortedRange:options:usingComparator:]) to find the nearest two elements. If you're only going to probe the array once or twice, just use a for loop, subtraction, abs, and MIN.
The easiest way is subtract the smaller number from the larger one. So you'd want to compare the two numbers first, then just do simple subtraction. So you'd see the 10-7 is 3 away, and 7-5 is only 2 away.

Algorithm for max and min? (Objective-C)

This is a part of a book I'm reading to learn Objective-C.
The following defines a macro called MAX that gives the maximum of two
values: #define MAX(a,b) ( ((a) > (b)) ? (a) : (b) )
And then there are some exercises in the book that asks the reader to define a macro (MIN) to find the minimum of two values and another that asks to define a macro called MAX3 that gives the maximum of 3 values. I think these two definitions will look similar to MAX, but I don't understand how the MAXformula finds the maximum value. I mean if I just did this
int limits = MAX (4,8)
It'll just assign limits the value of 8. What does that have to do with finding a variable's maximum value?
I think you are confusing value and variable. The macro example you listed expands to a comparison between two values and returns the greater of the two values (i.e. which is greater, a or b). So you are right, int limits = MAX(4,8) just assigns 8 to limits and has nothing to do with finding the maximum value you can store in limits.
The header limits.h defines many values like INT_MAX that will tell you information about the min/max values of variable types on your system.
To break it apart:
The declaration:
#define MAX(a,b)
If a is greater than b, use a else use b:
( ((a) > (b)) ? (a) : (b) )
Then to create a MIN expression, use a similar form:
#define MIN(a,b) ( ((a) < (b)) ? (a) : (b) )
^
Then to create a MAX3 expression, you can combine them:
#define MAX3(a,b,c) ( MAX(a, MAX(b,c)) )
Specifically, this macro's intended to be used with scalars (C builtins) which can be compared using < or >. If you passed an objc variable, it would result in comparison of addresses and MAX would return the one with the higher address (it would be very rare if you actually wanted to compare addresses of objc instances).
Also note that this is the classic example of how macros can bite you. With macros, the preprocessor simply expands (textual copy/paste) the parameters in place, so: int limits = MAX (4,8) literally expands to int limits = (4 > 8 ? 4 : 8). If you write MAX(x,++y), then y will be incremented twice if y is greater than or equal to x because it expands to: int limits = (x > ++y ? x : ++y).
generally, you will use a MAX() or MIN() macro to get whichever is the higher/lower of a pair of variables, or of a variable and a constant, or even a pair of macro constants or other non-literal constant expressions. you generally won't supply 2 literal constants as you have done in your question.
Algorithm for max (Objective-C)
// get max value
- (float)maxValue:(NSArray *)arrValue
{
float maxValue = 0.0;
for (NSString *value in arrValue) {
float compareValue = [value floatValue];
if (compareValue > maxValue) {
maxValue = compareValue;
}
}
return maxValue;
}
NSArray *number=[NSArray arrayWithObjects:[NSNumber numberWithFloat:57.02], [NSNumber numberWithFloat:55.02], [NSNumber numberWithFloat:45.02], nil];
NSLog(#"%f", [self maxValue:number]);
result 57.020000

If statetement : I found something strange. Why is my second 'if' is equal to no?

If statetement : I found something strange. Why does my second 'if' is equal to no?
int a = bookSettings.tracks.count;
int b = _currentChapter - 1;
NSLog(#"a: %d", a);
NSLog(#"b: %d", b);
if (a > b)
NSLog(#"yes 1");
else NSLog(#"no 1");
if (bookSettings.tracks.count > (_currentChapter -1))
NSLog(#"yes 2");
else NSLog(#"no 2");
and log
a: 27
b: -1
yes 1
no 2
NSArray's -count method returns an NSUInteger -- an unsigned integer.
Using bookSettings.tracks.count in your if statement is likely causing both sides of the expression to be cast as NSUInteger. Even though _currentChapter - 1 equals -1, as an unsigned integer it is a very large number. So your track count is obviously less than that very large integer, which is why the if statement is yielding "no 2".
I believe that _currentChapter is a NSUInteger. When you assign it to b it becomes a signed integer and is therefore able to use -1. However, bookSettings.tracks.count is a unsigned integer and since _currentChapter is an unsigned integer as well, when you subtract 1 from _currentChapter (0, in this case) it actually becomes a very high integer.
you have not mentioned the type of bookSettings.tracks.count and if there is a mismatch between datatypes or object-types , the question comes down to true or false and the if statement will execute accordingly.
There might also be difference between NSInteger ans trivial c int.

Objective-C: Strange calculation result

I am learning Objective-C and have completed a simple program and got an unexpected result. This program is just a multiplication table test... User inputs the number of iterations(test questions), then inputs answers. That after program displays the number of right and wrong answers, percentage and accepted/failed result.
#import <Foundation/Foundation.h>
int main (int argc, const char * argv[])
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSLog(#"Welcome to multiplication table test");
int rightAnswers; //the sum of the right answers
int wrongAnswers; //the sum of wrong answers
int combinations; //the number of combinations#
NSLog(#"Please, input the number of test combinations");
scanf("%d",&combinations);
for(int i=0; i<combinations; ++i)
{
int firstInt=rand()%8+1;
int secondInt=rand()%8+1;
int result=firstInt*secondInt;
int answer;
NSLog(#"%d*%d=",firstInt,secondInt);
scanf("%d",&answer);
if(answer==result)
{
NSLog(#"Ok");
rightAnswers++;
}
else
{
NSLog(#"Error");
wrongAnswers++;
}
}
int percent=(100/combinations)*rightAnswers;
NSLog(#"Combinations passed: %d",combinations);
NSLog(#"Answered right: %d times",rightAnswers);
NSLog(#"Answered wrong: %d times",wrongAnswers);
NSLog(#"Completed %d percent",percent);
if(percent>=70)NSLog(#"accepted");
else
NSLog(#"failed");
[pool drain];
return 0;
}
Problem (strange result)
When I input 3 iterations and answer 'em right, i am not getting of 100% right. Getting only
99%. The same count I tried on my iPhone calculator.
100 / 3 = 33.3333333... percentage for one right answer (program displays 33%. The digits after mantissa getting cut off)
33.3333333... * 3=100%
Can someone explain me where I went wrong? Thanx.
This is a result of integer division. When you perform division between two integer types, the result is automatically rounded towards 0 to form an integer. So, integer division of (100 / 3) gives a result of 33, not 33.33.... When you multiply that by 3, you get 99. To fix this, you can force floating point division by changing 100 to 100.0. The .0 tells the compiler that it should use a floating point type instead of an integer, forcing floating point division. As a result, rounding will not occur after the division. However, 33.33... cannot be represented exactly by binary numbers. Because of this, you may still see incorrect results at times. Since you store the result as an integer, rounding down will still occur after the multiplication, which will make it more obvious. If you want to use an integer type, you should use the round function on the result:
int percent = round((100.0 / combinations) * rightAnswers);
This will cause the number to be rounded to the closest integer before converting it to an integer type. Alternately, you could use a floating point storage type and specify a certain number of decimal places to display:
float percent = (100.0 / combinations) * rightAnswers;
NSLog(#"Completed %.1f percent",percent); // Display result with 1 decimal place
Finally, since floating point math will still cause rounding for numbers that can't be represented in binary, I would suggest multiplying by rightAnswers before dividing by combinations. This will increase the chances that the result is representable. For example, 100/3=33.33... is not representable and will be rounded. If you multiply by 3 first, you get 300/3=100, which is representable and will not be rounded.
Ints are integers. They can't represent an arbitrary real number like 1/3. Even floating-point numbers, which can represent reals, won't have enough precision to represent an infinitely repeating decimal like 100/3. You'll either need to use an arbitrary-precision library, use a library that includes rationals as a data type, or just store as much precision as you need and round from there (e.g. make your integer unit hundredths-of-a-percent instead of a single percentage point).
You might want to implement some sort of rounding because 33.333....*3 = 99.99999%. 3/10 is an infinite decimal therefore you need some sort of rounding to occur (maybe at the 3rd decimal place) so that the answer comes out correct. I would say if (num*1000 % 10 >= 5) num += .01 or something along those lines multiply by 100 moves decimal 3 times and then mod returns the 3rd digit (could be zero). You also might only want to round at the end once you sum everything up to avoid errors.
EDIT: Didn't realize you were using integers numbers at the end threw me off, you might want to use double or float (floats are slightly inaccurate past 2 or 3 digits which is OK with what you want).
100/3 is 33. Integer mathematics here.