How to format this NSString correctly? - objective-c

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.

Related

NSNumber stringValue different from NSNumber value

I'm having problems with converting NSNumber to string and string to NSNumber.
Here's a sample problem:
NSString *stringValue = #"9.2";
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
NSLog(#"stringvalue:%#",[[formatter numberFromString: stringValue] stringValue]);
Output will be:
stringvalue:9.199999999999999
I need to retrieve the original value, where, in the example should be 9.2.
On the contrary, when the original string is 9.4 the output is still 9.4.
Do you have any idea how to retrieve the original string value without NSNumber doing anything about it?
You are discovering that floating point numbers can't always be represented exactly. There are numerous posts about such issues.
If you need to get back to the original string, then keep the original string as your data and only convert to a number when you need to perform a calculation.
You may want to look into NSDecimalNumber. This may better fit your needs.
NSString *numStr = #"9.2";
NSDecimalNumber *decNum = [NSDecimalNumber decimalNumberWithString:numStr];
NSString *newStr = [decNum stringValue];
NSLog(#"decNum = %#, newStr = %#", decNum, newStr);
This gives 9.2 for both values.

NSNumberFormatter customize?

I wish to use NSNumberFormatter to merely attached a percent ('%') to the supplied number WITHOUT having it multiplied by 100.
The canned kCFNumberFormatterPercentStyle automatically x100 which I don't want.
For example, converting 5.0 to 5.0% versus 500%.
Using the following:
NSNumberFormatter *percentFormatter = [[NSNumberFormatter alloc] init];
[percentFormatter setNumberFormat:#"##0.00%;-##0.00%"];
But 'setNumberFormat' doesn't exist in NSNumberFomatter.
I need to use this NSNumberFormatter for my Core-Plot label.
How can I customize NSNumberFormat?
Ric.
Source: Apple's NSDecimalNumber reference.
Apparently I hinted the answer by saying that I didn't want the output to be 100x.
I'm working with a NSDecimalNumber which has the 'setMultiplier' method.
So, after I used the canned kCFNumberFormatterPercentStyle for the formatter, I used 'setMultiplier:1' as follows:
NSNumberFormatter *percentFormatter = [[NSNumberFormatter alloc] init];
[percentFormatter setNumberStyle:kCFNumberFormatterPercentStyle];
[percentFormatter setMultiplier:[NSNumber numberWithInt:1]];
Have you tried using setMultiplier to prevent it from multiplying by 100?
NSNumberFormatter *percentFormatter = [[NSNumberFormatter alloc] init];
[percentFormatter setNumberStyle: NSNumberFormatterPercentStyle];
[percentFormatter setMultiplier:1];
If adding the percent sign is all you need to accomplish, an alternative using NSNumberFormatterwould be:
[NSString stringWithFormat:#"%3.2f%%", [myNumber doubleValue]];
And you should adjust the precision specifier (3.2) to suit the number of digits you want to display.

Prevent small negative numbers printing as "-0"

If I do the following in Objective-C:
NSString *result = [NSString stringWithFormat:#"%1.1f", -0.01];
It will give result #"-0.0"
Does anybody know how I can force a result #"0.0" (without the "-") in this case?
EDIT:
I tried using NSNumberFormatter, but it has the same issue. The following also produces #"-0.0":
double value = -0.01;
NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
[numberFormatter setNumberStyle:NSNumberFormatterDecimalStyle];
[numberFormatter setMaximumFractionDigits:1];
[numberFormatter setMinimumFractionDigits:1];
NSString *result = [numberFormatter stringFromNumber:[NSNumber numberWithDouble:value]];
I wanted a general solution, independent of the configuration of the number formatter.
I've used a category to add the functionality to NSNumberFormater;
#interface NSNumberFormatter (PreventNegativeZero)
- (NSString *)stringFromNumberWithoutNegativeZero:(NSNumber *)number;
#end
With the implementation:
#implementation NSNumberFormatter (PreventNegativeZero)
- (NSString *)stringFromNumberWithoutNegativeZero:(NSNumber *)number
{
NSString *const string = [self stringFromNumber: number];
NSString *const negZeroString = [self stringFromNumber: [NSNumber numberWithFloat: -0.0f]];
if([string isEqualToString: negZeroString])
{
NSString *const posZeroString = [self stringFromNumber: [NSNumber numberWithFloat: 0.0]];
return posZeroString;
}
return string;
}
#end
How it works
The key feature is to ask the number formatter how it will format -0.0f (i.e., floating point minus zero) as an NSString so that we can detect this and take remedial action.
Why do this? Depending on the formatter configuration, -0.0f could be formatted as: #"-0", #"-0.0", #"-000", #"-0ºC", #"£-0.00", #"----0.0", #"(0.0)", #"😡𝟘.⓪零" really, pretty much anything. So, we ask the formatter how it would format -0.0f using the line: NSString *const negZeroString = [self stringFromNumber: [NSNumber numberWithFloat: -0.0f]];
Armed with the undesired -0.0f string, when an arbitrary input number is formatted, it can be tested to see if it is matches the undesirable -0.0f string.
The second important feature is that the number formatter is also asked to supply the replacement positive zero string. This is necessary so that as before, its formatting is respected. This is done with the line: [self stringFromNumber: [NSNumber numberWithFloat: 0.0]]
An optimisation that doesn't work
It's tempting to perform a numerical test yourself for whether the input number will be formatted as the -0.0f string, but this is extremely non trivial (ie, basically impossible in general). This is because the set of numbers that will format to the -0.0f string depend on the configuration of the formatter. If if happens to be rounding to the nearest million, then -5,000f as an input would be formatted as the -0.0f string.
An implementation error to avoid
When input that formats to the -0.0f string is detected, a positive zero equivalent output string is generated using [self stringFromNumber: [NSNumber numberWithFloat: 0.0]]. Note that, specifically:
The code formats the float literal 0.0f and returns it.
The code does not use the negation of the input.
Negating an input of -0.1f would result in formatting 0.1f. Depending on the formatter behaviour, this could be rounded up and result in #"1,000", which you don't want.
Final Note
For what it's worth, the approach / pattern / algorithm used here will translate to other languages and different string formatting APIs.
Use a NSNumberFormatter. In general, NSString formatting should not be used to present data to the user.
EDIT:
As stated in the question, this is not the correct answer. There is a number of solutions. It's easy to check for negative zero because it is defined to be equal to any zero (0.0f == -0.0f) but the actual problem is that a number of other values can be rounded to the negative zero. Instead of catching such values, I suggest postprocessing - a function that will check if the result contains only zero digits (skipping other characters). If yes, remove leading minus sign.
NSString *result = [NSString stringWithFormat:#"%1.1f", -0.01*-1];
If instead of a value you pass an instance you can check:
float myFloat = -0.01;
NSString *result = [NSString stringWithFormat:#"%1.1f", (myFloat<0? myFloat*-1:myFloat)];
Edit:
If you just want 0.0 as positive value:
NSString *result = [NSString stringWithFormat:#"%1.1f",(int)(myFloat*10)<0?myFloat:myFloat*-1];
Convert the number to NSString by taking the float or double value.
Convert the string back to NSNumber.
NSDecimalNumber *num = [NSDecimalNumber decimalNumberWithString:#"-0.00000000008"];
NSString *st2 = [NSString stringWithFormat:#"%0.2f", [num floatValue]];
NSDecimalNumber *result = [NSDecimalNumber decimalNumberWithString:st2]; //returns 0
The NSNumberFormatter has two methods convert from Number to String, and from String to Number. What if we use method (Number) -> String? twice?
public extension NumberFormatter {
func stringWithoutNegativeZero(from number: NSNumber) -> String? {
string(from: number)
.flatMap { [weak self] string in self?.number(from: string) }
.flatMap { [weak self] number in self?.string(from: number) }
}
}

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.

Dynamicaly rounding numbers in Objective-C

I know that there are many different questions about this sort of topic on SO already, but I couldn't find a way to tailor them all to my specific needs.
What I have is a floating point number that gets sent to me through the network that I need to convert and graph out to the screen. The numbers can range from 5.2, 285.159, 294729172.258, -10734.112, etc. What I would like to do is get the value used to round from one digit below the most significant digit.
Example:
5.2 = 5
285.159 = 300
294729172.258 = 300000000
-10734.112 = -11000
Any advice that can be used to help guide me would be greatly appreciated.
Here's my solution:
int roundMostSignificant(float input)
{
NSNumber *number = [NSNumber numberWithFloat:input];
static NSNumberFormatter *formatter = nil;
if (!formatter)
{
formatter = [NSNumberFormatter new];
[formatter setMinimumSignificantDigits:1];
[formatter setMaximumSignificantDigits:1];
[formatter setUsesSignificantDigits:YES];
}
return [[formatter numberFromString:[formatter stringFromNumber:number]] intValue];
}
Yes, this uses objects, but I think that this will be your best bet in the long run, as it handles rounding, parsing, etc. for you.
There is a NSDecimalNumber and NSDecimalNumberHandler classes which does just that. You can define to which precision and to which direction the numbers should be rounded.
Simple example might be:
NSDecimalNumber *dn = [NSDecimalNumber decimalNumberWithMatnissa:294729172258 exponent:-3 isNegative:NO];
NSDecimalNumberHandler *dnh = [NSDecimalNumberHandler decimalNumberHandlerWithRoundingMode:NSRoundPlain scale:-6 raiseOnExactness:NO raiseOnOverflow:NO raiseOnUnderflow:NO raiseOnDivideByZero:YES];
NSDecimalNumber *rounded = [dn decimalNumberByRoundingAccordingToBehavior:dnh];
This would probably work for your biggest number.