NSNumberFormatter: string to Double - cocoa-touch

I am trying to take a user input from a text field and format it into a double value for core data. Currently my code looks like:
NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
[numberFormatter setNumberStyle:NSNumberFormatterDecimalStyle];
NSNumber *weightDouble = [numberFormatter numberFromString:#"weight.text"];
However, if I print weightDouble I get 0, if I print out the text from the UI input I get the correct number. Any ideas on what I am doing wrong here? I get no errors on the build and it operates and saves fine (other than saving 0 no matter the input)

First of all:
(If 'weight' is a UITextField)
NSNumber *weightDouble = [numberFormatter numberFromString:weight.text];
With numberFromString:#"weight.text" you will be getting the value of the text 'weight.text' which is in fact 0.
But why not just double weightDouble = weight.text.doubleValue? (Except maybe localization concerns)

Related

NSNumberFormatter Scientific Form Output

I have NSNumber and NSResultFormatter, that converts number into string and displays it on screen.
In scientific form output for large numbers is "1.345e10" and for small numbers is "1.345e-10".
I want to output large numbers as "1.345e+10", like in standart iOS calc app. How can I to do it?
You can achieve that by setting a custom number format, for example:
NSNumberFormatter *fmt = [[NSNumberFormatter alloc] init];
[fmt setExponentSymbol:#"e"];
[fmt setPositiveFormat:#"0.###E+0"];
NSString *s = [fmt stringFromNumber:#(12345678900)];
// 1.235e+10
Custom number formats are documented in http://www.unicode.org/reports/tr35/tr35-25.html#Number_Format_Patterns.

How do I force a sign-character on the output of an NSNumberFormatter

I want to use a number formatter to generate my output, so the number is automatically formatted for the user's locale, but I want it to work like "%+.1f" does in printf(), that is always have a sign specified.
NSNumberFormatter *nf = [[NSNumberFormatter alloc] init];
nf.numberStyle = NSNumberFormatterDecimalStyle;
nf.maximumFractionDigits = 1;
double val = 3.1234;
label.text = [NSString stringWithFormat: #"XXX %# XXX", [nf stringFromNumber: [NSNumber numberWithDouble: val]]];
I want the label to come out "XXX +3.1 XXX" in the US and the appropriate but equivalent string for any other location. The only things I can find are setPositiveFormat: and setPositivePrefix:.
But I don't want to set the format since I don't know how to format numbers in other countries; I don't know if a plus-sign is used to designate a positive number in Arabic or Russian or some culture I have not thought of. I do know, for example, that decimal points, commas, spaces, etc., all have different meanings in European countries compared to the U.S. - Could the same be true for +/- signs?
What I do currently is:
label.text = [NSString stringWithFormat: #"XXX %s%# XXX", (val < 0) ? "" : "+",
[nf stringFromNumber: [NSNumber numberWithDouble: val]]];
But this presumes that '+' and '-' are correct for all formats.
I'm sure it must be there since it is a standard formatting thing that has been in printf() since the dark ages...
How about this:
NSNumberFormatter *nf = [[NSNumberFormatter alloc] init];
nf.numberStyle = NSNumberFormatterDecimalStyle;
nf.maximumFractionDigits = 1;
double val = 3.1234;
NSString *sign = (val < 0) ? [nf minusSign] : [nf plusSign];
NSString *num = [nf stringFromNumber:#(abs(val))]; // avoid double negative
label.text = [NSString stringWithFormat: #"XXX %#%# XXX", sign, num];
You may need to check to see if num has the sign prefix or not so it isn't shown twice.
Edit: After some playing around, it has been determined, for the "Decimal" style, that no current locale uses a positivePrefix. No current locale uses a plusSign other than the standard + character. No current locale uses a negativePrefix that is different than minusSign. No current locale uses either positiveSuffix or negativeSuffix.
So an easier approach would be to do:
NSNumberFormatter *nf = [[NSNumberFormatter alloc] init];
nf.numberStyle = NSNumberFormatterDecimalStyle;
nf.maximumFractionDigits = 1;
[nf setPositivePrefix:[nf plusSign]];
[nf setNegativePrefix:[nf minusSign]];
label.text = [nf stringFromNumber:#(val)];
This case it's simple, just add the prefix:
nf.positivePrefix= nf.plusSign;
Though it won't use the user's locale, you can do the following to generate the +/- sign without the somewhat expensive overhead of an NSNumberFormatter:
// assume 'number' is an NSNumber
label.text = [NSString stringWithFormat:#"%+.02f", [number floatValue]];
Simple Case:
let f = NumberFormatter()
f.positivePrefix = f.plusSign
Currency Case :
Hack needed, because setting the prefix to plusSign only will remove the currency symbol.
let f = NumberFormatter()
f.numberStyle = .currency
f.positivePrefix = f.plusSign + f.currencySymbol
There is a bit more work depending on the locale.. The currency symbol may be before, or after, but this is probably another subject..
Edit:
Even if it is another subject, I'd say a possible solution to the problem above is to subclass NSNumberFormatter :
override func string(from number: NSNumber) -> String? {
returns ( number.doubleValue >= 0 ? super.plusSign : "" ) + super.string(from: number)
}
This way, NSNumberFormatter should manage the currency position while your subclass simply prepend the + sign. No time to test this in depth, but at least it is an approach.
The underlying formatting language for NSNumberFormatter doesn't have any provision for what you want to do -- it will allow you to specify a localized positive sign on exponents, but not for the entire formatted string. Nor does NSLocale seem to make available the localized positive sign.
Aside from making a dummy string that includes an exponent, pulling the localized positive sign out, and putting your final formatted string together by hand, I think you're out of luck.
A reusable formatter in swift:
var numberFormatter: NSNumberFormatter {
let formatter = NSNumberFormatter()
formatter.numberStyle = .DecimalStyle
formatter.locale = NSLocale(localeIdentifier: "it_IT")//your Locale
formatter.maximumFractionDigits = 2
formatter.minimumFractionDigits = 0
formatter.positivePrefix = formatter.plusSign
return formatter
}
Then use it:
let myDoubleValue = 12.00
let myStringNumber = numberFormatter.stringFromNumber(myDoubleValue)!
I don't think any of the previous answers will actually take into consideration everything you mentioned in your question.
It is true that NumberFormatter does not have an option to set the plus sign visible for all positive numbers when formatting currency values.
Also, replacing prefixes and suffixes will likely break the format for some regions and always replacing a prefix will only work if the set locale uses the currency symbol on the left.
A simple way to address this without losing the locale formatting can be seen below:
var value: Double = 3.1234
let formatter = NumberFormatter()
formatter.numberStyle = .currency
formatter.maximumFractionDigits = 1
if value > 0 {
return formatter.string(for: value.negated())?.replacingOccurrences(
of: formatter.minusSign,
with: formatter.plusSign
)
} else {
return formatter.string(for: value)
}
Even though this can be seen as hack, it's an effective way to achieve everything you mentioned without manually writing a number formatter.

How to round a float to 2 decimal places?

This is my algorithm to find out the speed of my game.
self.speed=.7-self.score/50;
Now how can I make self.speed round to 2 decimal places?
Note: my answer assumes you only care about the number of decimals for the purpose of displaying the value to the user.
When you setup your NSNumberFormatter to format the number into a string for display, setup the formatter with a maximum of two decimal places.
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
[formatter setNumberStyle:NSNumberFormatterDecimalStyle];
[formatter setMaximumFractionDigits:2];
NSString *formattedNumber = [formatter stringFromNumber:#(self.speed)];
You have the option of using the setRoundingMode: method if you need a specific round method.
BTW - you shouldn't use a string format for this because it doesn't take the user's locale into account to format the number properly.
floats are handled in IEEE754 format, you can't directly decide how many decimal places will be used.You can directly decide how many bits will be used, or indirectly round the las part of the number doing this:
NSString* str=[NSString stringWithFormat: #"%.2f", number];
number= atof([str UTF8String]);
But like maddy pointed, you only need to round/truncate the unwanted decimal digits only when presenting the number to the user, so you could only use the %.2f format specifier when printing it, or use a formatter.
self.speed = (int)(self.speed * 100 + 0.5) / 100.0;
if you want to have that as a string:
NSString *speedString = [NSString stringWithFormat: #"%.2f", self.speed];

NSString to NSNumber with some decimal separator

The app I'm building now has a possibility to "download" text files and get the numbers from there. In the simulator everything works perfectly, but when I tested it on a device it just crashed. After a while I figured out that the problem was with the decimal separators. In that file I used . and the local setting of my iPhone require ,. Example of that string:
Product 1;100.00
Product 2;82.85
Product 3;95.12
//etc...
After changing the the first few . into , I could successfully run the app till it reached the first ., so that's the problem.
I can easily replace all . into , programmatically, BUT I want this app to work in any country with any number format and not limit it to some specific market.
The code I was using to get these numbers:
NSString *fileContents = [[NSUserDefaults standardUserDefaults]objectForKey:theKey];
NSArray *allLinedStrings = [fileContents componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]];
NSMutableArray *allStringNormalized = [NSMutableArray array];
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
[formatter setNumberStyle:NSNumberFormatterDecimalStyle];
for (int i = 0; i < allLinedStrings.count; i++) {
NSString *bla = [allLinedStrings objectAtIndex:i];
NSArray *bla1 = [bla componentsSeparatedByString:#";"];
[allStringNormalized addObject:[formatter numberFromString:[bla1 objectAtIndex:1]]];
}
self.values = [NSArray arrayWithArray:allStringNormalized];
Any help would be appreciated.
Thank you :)
If I understand the problem correctly, you want NSNumberFormatter to always use . as the decimal separator, regardless of the phone's locale. The solution to this is simple: use the instance methods setDecimalSeparator: and setCurrencyDecimalSeparator: of NSNumberFormatter to set the decimal separator to ..
Note that you don't need to set both decimal separators. You use setDecimalSeparator: if your numberStyle is NSNumberFormatterDecimalStyle and setCurrencyDecimalSeparator: if your numberStyle is NSNumberFormatterCurrencyStyle.
See the documentation for more details.

How to format this NSString correctly?

I want to format a string that can look like that:
0.0580 which means 5.8 ct
0.1580 which means 15.8 ct
1.1580 which means 1.15 €
So the string can be anything in x.xxxx format. Now I started formating it but I am new to objective-c and iOS.
First I want to remove the last character because the last number does not really matter and I don't want to round numbers.
NSString *responseString = [responseData
substringWithRange:NSMakeRange(1,
[responseData length]-2)];
This gives me x.xxx so far. Any idea how to proceed and what code to use? Are there any libraries on that?
Take a look at the NSNumberFormatter class. It should do what you need. Something like this:
NSNumberFormatter *numFormatter = [[NSNumberFormatter alloc] init];
NSNumber *myNumber = [NSNumber numberWithDouble:[#"0.158" doubleValue]];
[numFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
NSString *formattedValue = [numFormatter stringFromNumber:myNumber];
[numFormatter release];
Also look at NSNumberFormatterStyle and NSNumberFormatterBehavior to control the format.
Once you have your number in the form x.xxx, you could do something like:
float floatValue = [#"0.158" floatValue]; // Get your string as a number.
floatValue *= 100; // Turn '0.158' into '1.58'
Does this answer your question? I'm not quite sure that it does, so update your question and I will try to assist you better.