"isnan" doesn't seem to work - ios

Simply trying to catch non numeric input
Read MANY items . Tried decimalDigitCharacterSet (found it hard to believe that something that starts with the word "decimal" doesn't contain a decimal). Tried mutable character set to add the decimal. Been working to include "10.5" with "96" and still exclude "abc".
the following code produces "IS a number" no matter what I put in textbox1
double whatTheHey;
whatTheHey = _textBox1.text.doubleValue;
if isnan(whatTheHey) {
_textBox2.text = #"NOT a number > ";
}
if (!isnan(whatTheHey)) {
_textBox2.text = #"IS a number > ";
}
10.5 , 99 , qwerty all yield "IS a number"
This seems like a heck of a lot of work just to catch non numeric input.
Does anybody have any blatantky simple examples of working code to catch non numeric but accept numbers with decimal in them?

NaN does not literally mean "anything that is not a number". It is the name of a specific value — namely one floats can have after certain indeterminate operations such as dividing zero by zero or taking the square root of a negative number. See the Wikipedia entry for more history.
To actually parse the numeric value of a string, you probably want to look into NSNumberFormatter:
NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
[numberFormatter setNumberStyle:NSNumberFormatterDecimalStyle];
NSNumber *a = [numberFormatter numberFromString:#"10.5"];
NSNumber *b = [numberFormatter numberFromString:#"96"];
NSNumber *c = [numberFormatter numberFromString:#"abc"];
NSLog(#"a: %#, b: %#, c: %#", a, b, c);
Yields:
a: 10.5, b: 96, c: (null)
A simpler (if less flexible) solution that meets your specific criteria might be:
BOOL isNumber(NSString *aString){
return [aString integerValue] || [aString floatValue];
}
But if you're writing for iOS (or OS X), you really ought to get comfortable with the NSFormatters. They'll make your life a lot easier.

to check wether a string is numeric or not use the following piece of code.
NSString *newString = #"11111";
NSNumberFormatter *nf = [NSNumberFormatter new];
BOOL isDecimal = [nf numberFromString:newString] != nil;

Try this. I think it should do what you need:
- (BOOL)isStringNumeric:(NSString *)input {
NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
NSNumber *number = [numberFormatter numberFromString:input];
return (number != nil);
}

Related

NSNumberFormatter with number style NSNumberFormatterCurrencyStyle trouble with commas

I'm trying to do things right with input expenses on some items. So, when working on a device using a locale that uses comma separated decimals (the decimal pad automatically sets comma ',' instead of dot '.' for the user to input) I store the value using core data in a double variable converting the text this way:
NSNumberFormatter * f = [[NSNumberFormatter alloc] init];
[f setNumberStyle:NSNumberFormatterDecimalStyle];
[f setMaximumFractionDigits:2];
NSNumber * gasto = [f numberFromString:(((UITextField*) [cell viewWithTag:11]).text)];
destino.nuevaTrans.gasto = [gasto doubleValue];
//destino.nuevaTrans is the managed object, nothing weird there, just storing the decimal value
But when taking the value out of the stored objects and showing them in a table view I'm losing the decimals because of the comma separated locale settings (?). Doing it this way:
etiq = (UILabel*) [cell.contentView viewWithTag:12];
NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
[numberFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
etiq.text = [numberFormatter stringFromNumber:#(trans.gasto)];
//debugging console output:
//remmember, trans.gasto is only a double
NSLog(#"a: %f, b: %#", trans.gasto ,[numberFormatter stringFromNumber:#(trans.gasto)]);
The output on the simulator that is using dot '.' locale settings works fine, but when deploying on the device that is using comma ',' settings the NSNumberFormatter doesn't seem to translate it correctly to currency style.
//output on the simulator:
2014-09-07 15:00:47.561 iSpend[3930:60b] a: 2.990000, b: $2.99
//output on the device:
2014-09-07 15:02:40.005 iSpend[1389:60b] a: 2.170000, b: ₡2
So, I could try and hack this thing out... But I'm looking for a better programming practice here. Every suggestion is appreciated! Thanks!
Each Locale determines if it shows cents or just Krona. So, if you want to force it to show digits after the comma, add the line:
[numberFormatter setMinimumFractionDigits:2];
to get two digits after comma.
But the example you show has correct output.
u can try to set separator clearly: [formatter setDecimalSeparator:#"."];

How do I get NSNumber with NSLocale smarts from a UITextField?

Getting correct localized formatting of numbers to display is easy.
But prompting for a decimal number input from a UITextField is proving tricky.
Even though the decimal keypad may present the comma for European usage instead of the stop, the method handling the input apparently still needs to be locale-savvy.
my research here on S.O. and other places suggests
-(IBAction)buttonPress:(UIButton *)sender
{
NSNumber *firstNumber = #([self.firstField.text floatValue]); // 2 alternative ways
NSNumber *secondNumber = [NSNumber numberWithFloat:[self.secondField.text floatValue]];
.
.
only gives me integers to perform arithmetic with. They aren't floats at all. They have no float value
Supposing this is the only thing the app does, and I immediately do:
.
.
NSNumberFormatter *numberFormat = [[ NSNumberFormatter alloc] init];
[ numberFormat setNumberStyle:NSNumberFormatterDecimalStyle];
[ numberFormat setLocale:[NSLocale currentLocale]];
[ numberFormat setMaximumFractionDigits:2];
NSString *displayString = [ numberFormat stringFromNumber:firstNumber];
self.resultLabel.text = displayString;
}
I am throwing the input back to a label in the ViewController without any further handling to make sure the the decimals aren't hidden by not having a formatter. No Joy. Still in integers.
Obviously the app has to do something in the way of calculation, and since one can't handle NSNumber wrappers directly, I have to resolve it before i do the conversion thing:
double xD = [firstNumber doubleValue];
double yD = [secondNumber doubleValue];
the question is, where is the decimal-ness of the input being lost?
I have no problem with the desktop keyboard and the simulator, but if i set a test device to - say - Polish then that simply won't do.
EDIT:
and here's the result from the answer provided below
// run these 4 methods first, that way you can re-use *numberFormat for the display
NSNumberFormatter *numberFormat = [[ NSNumberFormatter alloc] init];
[ numberFormat setNumberStyle:NSNumberFormatterDecimalStyle];
[ numberFormat setLocale:[NSLocale currentLocale]];
[ numberFormat setMaximumFractionDigits:2];
// here 'tis
NSNumber *firstNumber = [numberFormat numberFromString:self.firstField.text];
// do the same for other input UITextFields
// sh-boom. there you go
You need use longLongValue not longValue to convert NSString to long type. or use NSNumberFormatter->numberFromString:

Float value resets after Adding

I start off with a number, lets say 250. I add all sorts of numbers, but anytime I add a high number like 2,000 it adds correctly. Then I add 3. The new number comes out to 5 like it thought 2,000 was 2.0. I do not know why it is doing this.
float start = self.amountLabel.text.floatValue;
float changeAmount = self.amountField.text.floatValue;
float newValue;
if (determConfirm == 1) {
newValue = start + changeAmount;
} else {
newValue = start - changeAmount;
}
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
[formatter setNumberStyle:NSNumberFormatterDecimalStyle];
[formatter setMaximumFractionDigits:5];
[formatter setRoundingMode: NSNumberFormatterRoundUp];
NSString *numberString = [formatter stringFromNumber:[NSNumber numberWithFloat:newValue]];
[[NSUserDefaults standardUserDefaults] setValue:numberString forKey:#"newValue"];
[[NSUserDefaults standardUserDefaults] synchronize];
self.amountLabel.text = numberString;
self.amountField.text = #"0.00";
[self.amountField resignFirstResponder];
The problem is the use of floatValue to convert the formatted number text to a number. floatValue only works as expected if the text is unformatted (no commas) and uses the period for the decimal separator.
Since you store a formatted number in the field, it only works with small numbers and in certain locales.
Replace your use of floatValue on the text with the same NSNumberFormatter used to format the number. Use it to parse the text and give you an NSNumber (which you can then call floatValue on).
Just a guess as it's hard by looking at your code, but maybe you want this:
float newValue = start;
if (determConfirm == 1) {
newValue += changeAmount;
} else {
newValue -= changeAmount;
}

How to truncate an NSString 'x' characters after a certain character is found

Say I have an NSString, it represents a price that otherwise would be a double of course. I am trying to make it truncate the string at the hundredths place so it is something like 19.99 instead of 19.99412092414 for example. Is there a way, once detecting the decimal like so...
if ([price rangeOfString:#"."].location != NSNotFound)
{
// Decimal point exists, truncate string at the hundredths.
}
for me to cut off the string 2 characters after that ".", without separating it into an array then doing a max size truncate on the decimal before finally reassembling them?
Thank you very much in advance! :)
This is string manipulation, not math, so the resulting value won't be rounded:
NSRange range = [price rangeOfString:#"."];
if (range.location != NSNotFound) {
NSInteger index = MIN(range.location+2, price.length-1);
NSString *truncated = [price substringToIndex:index];
}
This is mostly string manipulation, tricking NSString into doing that math for us:
NSString *roundedPrice = [NSString stringWithFormat:#"%.02f", [price floatValue]];
Or you might consider keeping all numeric values as numbers, thinking of strings as just a way to present them to the user. For that, use NSNumberFormatter:
NSNumber *priceObject = // keep these sorts values as objects
NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
[numberFormatter setNumberStyle: NSNumberFormatterCurrencyStyle];
NSString *presentMeToUser = [numberFormatter stringFromNumber:priceObject];
// you could also keep price as a float, "boxing" it at the end with:
// [NSNumber numberWithFloat:price];

How do I set a currency string value to a UITextField, preserving encoding?

I am developing an application in which I wish to handle different currency formats, depending on the current locale. Using a NSNumberFormatter I can correctly translate a number into string and back without problems.
But, if I put the string value into a UITextField and later get it back, I won't be able to convert the string back into a number and I will get a nil value instead.
Here is a sample code to explain the problem:
NSNumberFormatter *nf = [Utils currencyFormatter];
NSNumber *n = [NSNumber numberWithInt:10000];
NSString *s = [nf stringFromNumber:n];
NSLog(#"String value = %#", s);
UITextField *t = [[UITextField alloc] init];
// I put the string into the text field ...
t.text = s;
// ... and later I get the value back
s = t.text;
NSLog(#"Text field text = %#", s);
n = [nf numberFromString:s];
NSLog(#"Number value = %d", [n intValue]);
where the currencyFormatter method is defined this way:
+ (NSNumberFormatter *)currencyFormatter
{
static NSNumberFormatter *currencyFormatter;
if (!currencyFormatter) {
currencyFormatter = [[NSNumberFormatter alloc] init];
[currencyFormatter setFormatterBehavior:NSNumberFormatterBehavior10_4];
[currencyFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
[currencyFormatter setLocale:[NSLocale currentLocale]];
if ([currencyFormatter generatesDecimalNumbers] || [[currencyFormatter roundingIncrement] floatValue] < 1) {
[currencyFormatter setGeneratesDecimalNumbers:YES];
[currencyFormatter setRoundingIncrement:[NSNumber numberWithFloat:0.01]];
}
}
return currencyFormatter;
}
(The inner if is used to force the formatter to always round to the smallest decimal digit, eg. even for CHF values).
What I get in the Console is this:
2012-03-29 00:35:38.490 myMutuo2[45396:fb03] String value = € 10.000,00
2012-03-29 00:35:38.494 myMutuo2[45396:fb03] Text field text = € 10.000,00
2012-03-29 00:35:38.497 myMutuo2[45396:fb03] Number value = 0
The strange part is that the spacing character between € and 1 in the first line is represented in the console through a mid-air dot, while in the second line this dot disappears. I believe this is an encoding-related problem.
Can anyone help me solve this problem?
Thank you!
Edit
I changed my test code to this:
NSNumberFormatter *nf = [Utils currencyFormatter];
NSNumber *n = [NSNumber numberWithInt:10000];
NSString *s = [nf stringFromNumber:n];
NSLog(#"String value = %# (space code is %d)", s, [s characterAtIndex:1]);
UITextField *t = [[UITextField alloc] init];
t.text = s;
s = t.text;
NSLog(#"Text field text = %# (space code is %d)", s, [s characterAtIndex:1]);
n = [nf numberFromString:s];
NSLog(#"Number value = %d", [n intValue]);
to discover this:
2012-03-29 02:29:43.402 myMutuo2[45993:fb03] String value = € 10.000,00 (space code is 160)
2012-03-29 02:29:43.405 myMutuo2[45993:fb03] Text field text = € 10.000,00 (space code is 32)
2012-03-29 02:29:43.409 myMutuo2[45993:fb03] Number value = 0
The NSNumberFormatter writes down the space as a non-breaking space (ASCII char 160), and then the UITextField re-encodes that space as a simple space (ASCII char 32). Any known workaround for this behaviour? Perhaps I could just make a replacement of the space with a non-breaking space but ... will it work for all the locales?
A possible workaround: You could try to parse only the number values (and punctiation) via a regex pattern and create you currency value based on that number. If you do it in that way it is perhaps even more forgivable for the user, if he typed another currency symbol or other symbols that shouldnt be there...
I was able to solve this problem only extending the UITextField through a custom class. In this new class I put a new #property of type NSString in which I store the "computed" string value for the text field. This string will never be modified and will preserve the original encoding of the content of the text field.
When you need to work again on the original untouched content of the text field you have to refer to this new property instead of referring to the text property.
Using a separate string container is the only way to avoid these strange encoding changes.

Resources