I am making a calculator that logs input in a label named "inputLabel' and then outputs the answer in a different label named "outputLabel" (similar to a graphing calculator). Once the user is finished entering the expression, the expression is stored in an NSString object and then parsed with the NSPredicate class and evaluated with the NSExpression class. What I have works, but I have noticed for particular operations the answers are not correct. For example, if the user types in "25/2" the calculator returns 12, which is obviously incorrect. However, if the user types in "25/2.0" or "25.0/2" the calculator returns 12.5 which is what I want. It seems that the NSExpression method 'expressionValueWithObject' is interpreting the operands as integers instead of floats. If this is the case, is there a way that I change the 'expressionValueWithObject'method to interpret the operands as floats?
Brain.m
-(float)performCalculation: (NSString *)operation
{
NSPredicate *parsed = [NSPredicate predicateWithFormat:[operation stringByAppendingString:#"=1.0"]];
NSExpression *inputExpressionParsed = [(NSComparisonPredicate *)parsed leftExpression];
NSNumber *result = [inputExpressionParsed expressionValueWithObject:inputExpressionParsed context:nil];
return [result floatValue];
}
ViewController.m
- (IBAction)equalsPressed:(id)sender
{
//self.inputLabel.text = [self.inputLabel.text stringByAppendingString:#".0"];
NSString *inputExpression = self.inputLabel.text;
self.inputLabel.text = [self.inputLabel.text stringByAppendingString:#"="];
float result = [self.brain performCalculation:inputExpression];
self.outputLabel.text = [NSString stringWithFormat:#"%g", result];
}
No, NSExpression cannot do that. You could try to append ".0" to all integer numbers
in the string before evaluating it, but the better solution is probably to use a "proper"
math expression parser, for example
https://github.com/davedelong/DDMathParser
You could iterate through the expression tree replacing the expression with the integer value (expressionType == NSConstantExpression). It depends a little bit of the features of your calculator, whether it is worth or not.
Related
I have two text fields and user entered the values. I can get the values of textFields like below
NSString *number1 = firstTextField.text;
NSString *number2 = secondTextField.text;
I want to multiply number1 and number2 without converting them into integer or number.I am doing like below
NSExpression *expression = [NSExpression expressionWithFormat:[NSString stringWithFormat:#"%#*%#",number1,number2]];NSLog(#"Multiplication result is----%#",[expression expressionValueWithObject:nil context:nil]);
I don't know if it is correct or not. If it is not correct please give me the suggestions how it can be possible.
By using NSExpression is one way to Multiply/Add/Subtract two number strings without converting them into integer or number.
NSExpression *expression = [NSExpression expressionWithFormat:expressionFormat];
You are using here expressionFormat as [NSString stringWithFormat:#"%#*%#",number1,number2];.
If you want to add or subtract number1 and number2 then replace * with + or -. In this way you have to proceed.
If the question was in an interview.
The interviewers were probably expecting you to write a method to go through both arrays and multiply the characters (converting one by one to integers) or (also identifying the represented character to know the equivalent integer number).
Searching on google there are some examples in different languages.
JAVA
http://csjobinterview.wordpress.com/2012/04/05/string-multiplication/
string multiplication
C++
Multiplying two number arrays
It is a common question in interviews.
I simply used textfields... and used the values that the user input into those textfields... here is one calculation example that has worked for me... in this example the user is trying to find the volume of beachstone they would need to order... the formula is pretty straight forward. Make sure you use the math brackets () to distinguish order of operations...
-(IBAction)calculate_beachstone:(id)sender {
float floatanswer =
([area1.text floatValue])+([area2.text floatValue])+([area3.text floatValue])+([area4.text floatValue])+([area5.text floatValue])+([area6.text floatValue])+([area7.text floatValue])+([area8.text floatValue])+([area9.text floatValue])+([area10.text floatValue]))
*([beachstone_depth.text floatValue])/12)/27);
NSString *stringRectResult=[[NSString alloc]
initWithFormat:#"%.1f",floatanswer];
answer_beachstone.text=stringRectResult;
}
I am adding the text found in the textfield (which user can only use numbers for input - custom keyboard)... in this example I have up to 10 fields which can be added together... then I use the * (multiply) to apply a depth in this example, and convert it back to a text string so I can display the result somewhere else...
In fact, if you write this part answer_beachstone to NSUserDefaults, you can use the result anywhere in different controllers, by calling it back.
If you're interested, here is how I did that...
-(IBAction)save_answer_beachstone:(id)sender {
save_answer_beachstone = [[NSString alloc] initWithString:answer_beachstone.text];
[answer_beachstone setText:save_answer_beachstone];
NSUserDefaults *save = [NSUserDefaults standardUserDefaults];
[save setObject:save_answer_beachstone forKey:#"save_answer_beachstone"];
}
Then I can use the resulting "answer" in any controller inside viewDidLoad...
[answer_beachstone setText:[[NSUserDefaults standardUserDefaults] objectForKey:#"save_answer_beachstone"]];
I know the question was asked 4 years ago, but this formula syntax works for me in a number of ways, in various apps...
You can't multiply two strings. It's not possible.
For this you have to convert it into Integer or int using NSNumber or using:
[secondTextField.text intValue].
NSInteger number = [firstTextField.text integerValue]*[secondTextField.text integerValue];
I need to sort a list of files according to the number after "_" :
"filename_1" "filename_2" ......
I know that I can extract the number, then sort the filename with the number, but I have to consider what if some illegal name exist and the program turn to be long.
What I want to do is just simply compare the whole String, the same char must has the same number. But when I do [#"word" intValue], the result is zero if the String is not a number. I want to know is there a good way to turn a String into a number.
Take a look at NSString's method:
- (NSComparisonResult)compare:(NSString *)aString options:(NSStringCompareOptions)mask
and the NSNumericSearch option.
If you wish to sort them on your own then you will need to use the
- (NSComparisonResult)compare:(NSString *)aString;
routine.
Here is the reference to the NSComparisonResult capable outputs.
These constants are used to indicate how items in a request are ordered.
enum {
NSOrderedAscending = -1,
NSOrderedSame,
NSOrderedDescending
};
typedef NSInteger NSComparisonResult;
Constants:
NSOrderedAscending
The left operand is smaller than the right operand.
NSOrderedSame
The two operands are equal.
NSOrderedDescending
The left operand is greater than the right operand.
If you think it easier, your can fill an NSArray with your separated strings, then use something like the following:
sortedArray = [anArray sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)];
Link to a good QA from SO:
How to sort a NSArray alphabetically?
There is 1 final way I can think of in your case. That would be to use the hasPrefix method of NSString to make sure that each of your strings begins with "filename_" as a first validation. afterwards substring the overall string just getting the remaining string after the "filename_". If all of your filenames are to have a number of 1 or greater, then a 0 means the string is invalid at this point, else the intValue should return a valid positive integer, and you can sort via the integer values.
This should work:
NSString *good = #"filename_1";
NSString *bad = #"filename";
NSCharacterSet *letterSet= [NSCharacterSet letterCharacterSet];
good = [good stringyByTrimmingCharactersInSet:letterSet]; // 1
bad = [bad stringByTrimmingCharactersInSet:letterSet]; // <empty>
You can use following coe to sort the array containing the list of File Objects:
NSSortDescriptor *aSortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"fileName" ascending:YES];
[arrayOfFileNames sortUsingDescriptors:[NSArray arrayWithObject:aSortDescriptor]];
Hope this is helpful to you...
I think you can not compare it with NSString because NSString follows 1,10,11,12,13,14 .....2,20,21,22 ......this type of ascending order while integer follows like 0,1,2,3,4,5,6,7....... and so on..... So you have to split the last character from string then will have to compare.
well how to short string in ascending order ....
NSMutableArray *array = [NSMutableArray arrayWithObjects:#"f_1",#"f_2",#"f_12",#"f_6",#"f_9",#"f_4", nil];
NSSortDescriptor *name = [[[NSSortDescriptor alloc] initWithKey:#"" ascending:YES] init];
[array sortUsingDescriptors:[NSMutableArray arrayWithObjects:name, nil]];
but this will give you string shorting not according to number.
Thank You!
Something weird just happened.
I stored a NSNumber with an unsigned long long value in NSUserDefaults. When I retrieve it, the value just changed. It seems that system thinks the number is long long instead of unsigned long long.
What's worse is that when I compare the number retrieved from UserDefaults with the original number, the result is NotEqual!
what's wrong with the code? Thank you!
static NSString * const NumberKey = #"MyNumber";
unsigned long long value = 15908045869032883218ULL;
if ([[NSUserDefaults standardUserDefaults] objectForKey:NumberKey] == nil) {
NSNumber *number = [NSNumber numberWithUnsignedLongLong:value];
[[NSUserDefaults standardUserDefaults] setObject:number forKey:NumberKey];
NSLog(#"Original Number:%#", number); // 15908045869032883218, right
}
NSNumber *number = [[NSUserDefaults standardUserDefaults] objectForKey:NumberKey];
NSLog(#"Current Number:%#", number); // -2538698204676668398, weird
NSLog(#"Current Value:%llu", [number unsignedLongLongValue]); // 15908045869032883218, right
NSLog(#"%d", [number isEqualToNumber:[NSNumber numberWithUnsignedLongLong:value]]); // 0
NSLog(#"%d", [number unsignedLongLongValue] == value); // 1
To further answer your question. If you look in the documentation for NSNumber's isEqualToNumber: function you will notice the following line,
Two NSNumber objects are considered equal if they have the same id values or if they have equivalent values
it's important you understand this. In your code you are asking is my NSNumber object "number" equal to "value", you are not asking does the numerical value stored within my NSNumber object "number" equal the numerical value stored within my NSNumber object "value".
The last line of code you have written shows that in fact your NSNumber's numerical values are in fact equal.
NSLog(#"%d", [number unsignedLongLongValue] == value); //1
So you are correctly storing and retrieving the values, you should be using the == comparison method with NSNumber objects stored numerical values (ie intValue == intValue, unsignedLongLongValue == unsignedLongLongValue) and not comparing their object id's together.
As for this line of code
NSLog(#"Current Number:%#", number); // -2538698204676668398, weird
This is not weird, this is perfectly normal, as you have told NSLog to print out an NSObject representation of 'number'. I'm not 100% certain but I believe that NSNumber's - ( NSString * ) description function defaults to return an unsigned int value for the numerical value it contains. This is why you are getting the large negative number returned. You may want to look at NSNumber's - (NSString *)descriptionWithLocale:(id)aLocale function to print out the data in a more logical for for you, or you could use
NSLog(#"Current Number:%llu", [number unsignedLongLongValue]);
Which will give you the right answer.
EDIT:
Further to this, after looking into the issue what is happening is that on recollection of your NSNumber object from UserDefaults it's original number type is not being preserved (this information is highlighted in the documentation for NSNumber in the overview section)
(Note that number objects do not necessarily preserve the type they are created with.)
You can see this yourself if you log the following after retrieving "number" from user defaults (add this to the end of the code you have in your question) and have a look at the encoding values shown here
NSLog(#"%s", [number objCType]); //This will log out q
NSLog(#"%s", [[NSNumber numberWithUnsignedLongLong:value] objCType]); //this will log out Q
The difference between Q and q is that Q is an unsigned value... hence why you are having issues with the isEqualToNumber: function as the number types are different.
If you are so dead set on using the iSEqualToNumber: function to compare values then you could implement this to retrieve your value from NSUserDefaults.
NSNumber *number = [NSNumber numberWithUnsignedLongLong:[[NSUserDefaults standardUserDefaults] objectForKey:NumberKey] unsignedLongLongValue]];
You could look at using the NSNumber compare: function to see if the returned value is NSOrderedSame however this will not work for comparing unsigned vs signed values of the same type so in your situation I'd use the above as retrieving the data from NSUserDefaults is stripping the "signedness" of your number.
At the end of the day if you want to store NSNumber into NSUserDefaults this code works for me even for large integers like: 881217446193276338
To save:
[[NSUserDefaults standardUserDefaults] setObject:self.myUser.sessionid forKey:#"sessionid"];
[[NSUserDefaults standardUserDefaults] synchronize];
To recover:
self.myUser.sessionid = (NSNumber *)[[NSUserDefaults standardUserDefaults] objectForKey:#"sessionid"];
It's storing it correctly, nothing is wrong with your code except:
NSLog(#"Current Number:%#", number);
Here number is a non-string object, you might think of it as a wrapper for a numerical primitive. Or you might think that NSNumber instances objectify a primitive type.
What you need is some thing like:
NSLog(#"Current Number:%#", [number stringValue]);
Here is a speculative answer:
The NSNumber documentation states that:
(Note that number objects do not necessarily preserve the type they are created with.) .
So it must be using a different internal storage for this type, and only gives you the correct value when you specifically ask for the unsigned long long value. The description method, which is called in your NSLog statement, may be defaulting to a different representation type.
And / or there may be some quirk of the unarchiver that is preventing the isEqualToNumber method working on the value returned from defaults. If you do that comparison between two NSNumbers created in the same scope, does it work? The correct value is definitely in there somewhere given your last statement returns true.
Why doesn't this please the compiler? Casting is supposed to work like in C as I can read here How to cast an object in Objective-C.
[p setAge:(NSNumber*)10];
where
- (NSNumber*) age {
return _age;
}
- (void) setAge: (NSNumber*)input {
[_age autorelease];
_age = [input retain];
}
In a sort of symmetry with your earlier question, NSNumber is an object type. You need to create it via a method invocation such as:
[p setAge:[NSNumber numberWithInt:10]];
Your current code is attempting simply to cast the arbitrary integer 10 to a pointer. Never do this: if the compiler didn't warn you about it, you would then be trying to access some completely inappropriate bytes at memory location 10 as if they were an NSNumber object, which they wouldn't be. Doing that stuff leads to tears.
Oh, and just to preempt the next obvious issues, remember that if you want to use the value in an NSNumber object, you need to get at that via method calls too, eg:
if ( [[p age] intValue] < 18 )
...
(NSNumber is immutable, and I think it is implemented such that identical values are mapped to the same object. So it is probably possible to get away with direct pointer comparisons for value equality between NSNumber objects. But please don't, because that would be an inappropriate reliance on an implementation detail. Use isEqual instead.)
Today it is also possible to do it using shorthand notation:
[p setAge:#(10)];
Use this:
[p setAge:[NSNumber numberWithInt:10]];
You can't cast an integer literal like 10 to a NSNumber* (pointer to NSNumber).
NSNumber *yourNumber = [NSNumber numberWithInt:your_int_variable];
Because NSNumber is an object and "10" in a primitive integer type, much like the difference between int and Integer in Java. You, therefore, need to call its initialiser:
[p setAge:[NSNumber numberWithInt:10]
I have strings that look about like this:
stringA = #"29.88";
stringB = #"2564";
stringC = #"12";
stringD = #"-2";
what is the best way to convert them so they can all be used in the same mathmatical formula?? that includes add, subtract.multiply,divide etc
Probably floatValue (as it appears you want floating-point values), though integerValue may also be of use (both are instance methods of NSString).
[stringA doubleValue]
These are all wrong, because they don't handle errors well. You really want an NSNumberFormatter.
If you have the string #"abc" and try to use intValue or floatValue on it, you'll get 0.0, which is obviously incorrect. If you parse it with an NSNumberFormatter, you'll get nil, which is very easy to distinguish from an NSNumber (which is what would be returned if it was able to parse a number).
Assuming that you have NSString variables.
NSString *stringA = #"29.88";
NSString *stringB = #"2564";
NSString *stringC = #"12";
NSString *stringD = #"-2";
suppose, you want to convert a string value to float value, use following statement.
float x=[stringA floatValue];
suppose, you want to convert a string value to integer value, use following statement.
NSInteger y = [stringC intValue];
Hope, it helps to you.