ObjC Formatting Decimal places of Float to string - iphone

How do I format a float to string so that if the user enters a number that has 2 or less decimal places then it is formatted to have 2 decimal places, but if the user enters a float that has 2 or more decimal places then all decimal places are shown. e.g.
4.1 => 4.10
1 => 1.00
1.358484 => 1.358484
So therefore the formatting is to 2 decimal places or more if needed.
Hope I made sense.

Try something like this.
BOOL requiresExtraPrecision (double num) {
double roundedToHundredth = round(num * 100.0) / 100.0;
double diff = num - roundedToHundredth;
if (diff < 0) diff = 0.0 - diff;
if (diff < 0.0000001) {
return NO;
} else {
return YES;
}
}
NSString *formatted (double num) {
if (requiresExtraPrecision(num)) {
return [NSString stringWithFormat:#"%F", num];
} else {
NSNumberFormatter *formatter = [[[NSNumberFormatter alloc] init] autorelease];
[formatter setMaximumFractionDigits:2];
[formatter setMinimumFractionDigits:2];
return [formatter stringFromNumber:[NSNumber numberWithDouble:num]];
}
}
As #Carl wrote in a comment to the question, the hard part is deciding when a double needs all of its precision. In this code, I'm assuming that if the double is "close enough" to a rounded number (within a millionth), then we should just display the rounded number.
You might decide to make it stricter (a billionth?) but you'll always have to use some kind of approximation, because some decimals can't be stored precisely as a float. Even though the user may have typed "0.1" at input time, that information is lost when the number is stored as a float.
So, given that you'll have a float that's extremely close to a decimal but not exactly right, you'll have to decide when you think the float is "close enough" to the decimal.
If you need absolute precision (if you're working with money!) then you should consider using an NSDecimal or an NSDecimalNumber instead of a float.

this could work for you:
NSNumber *aFloat = [NSNumber numberWithFloat:1.2]; //try 1.234 ; 1.23 ; 1.2 ; 1. ; 1
NSString *numberString = [aFloat stringValue];
NSRange dot;
dot = [numberString rangeOfString:#"."];
NSString *finalString;
if (dot.location != NSNotFound) {
NSString *decimals = [numberString substringFromIndex:dot.location + dot.length];
if (decimals.length<1){ // ends with "."
finalString = [numberString stringByAppendingString:#"00"];
}else if (decimals.length<2){ // ends with ".n"
finalString = [numberString stringByAppendingString:#"0"];
}else { // 2 or more decimals: no changes
finalString = numberString;
}
}else { // no decimals
finalString = [numberString stringByAppendingString:#".00"];
}
NSLog(#"______._____finalString:%#", finalString );
EDIT (more flexible, it works with variable numbers of decimals):
NSNumber *aFloat = [NSNumber numberWithFloat:1.1235]; //try 1.234 ; 1.23 ; 1.2 ; 1. ; 1
NSString *numberString = [aFloat stringValue];
NSRange dot;
dot = [numberString rangeOfString:#"."];
if (dot.location == NSNotFound) { // no decimals, add a dot
numberString = [numberString stringByAppendingString:#"."];
NSLog(#"__added dot___ numberString:%#", numberString );
}
dot = [numberString rangeOfString:#"."];
NSString *decimals = [numberString substringFromIndex:dot.location + dot.length];
// int initialDecimals = decimals.length;
int numberOfDecimalsTerget = 2;
for (int initialDecimals = decimals.length; initialDecimals<numberOfDecimalsTerget; initialDecimals++) {
numberString = [numberString stringByAppendingString:#"0"];
}
NSLog(#"__END_._____numberString:%#", numberString );

Related

Elegant method to omit fraction formatting number if number is an integer

I am formatting floating point numbers and right now I have the %0.2f formatter, but I'd like to omit the .00 if the floating point number is an even integer.
Of course I can think of string replacing the .00, but that's crude.
I found that the description of NSNumber also does something similar:
NSNumber *number = [NSNumber numberWithFloat:_paragraphSpacing];
[retString appendFormat:#"margin-bottom:%#px;", number];
This this does hover not limit the post comma digits. if the number is 1234.56789 then the description will output that.
So my question is, is there a just as simple way - possibly without having to create an NSNumber object - to achieve this result?
Since floating-point numbers aren't exact, there's no guarantee that your number will actually be an integer. You can, however, check if it's within a reasonably small distance from an integer value. And of course you don't need an NSNumber for this. (Generally speaking, NSNumber is not used for formatting, its purpose is representing a primitive C type, either integral or floating-point types, using an Objective-C object.)
#include <math.h>
- (NSString *)stringFromFloat:(float)f
{
const float eps = 1.0e-6;
if (abs(round(f) - f) < eps) {
// assume an integer
return [NSString stringWithFormat:#"margin-bottom: %.0fpx", round(f)];
} else {
// assume a real number
return [NSString stringWithFormat:#"margin-bottom: %.2fpx", f];
}
}
Use a formatter:
NSNumberFormatter* formatter= [NSNumberFormatter new];
formatter.numberStyle= NSNumberFormatterDecimalStyle;
formatter.maximumFractionDigits=2;
NSNumber *number = [NSNumber numberWithFloat:_paragraphSpacing];
[retString appendFormat:#"margin-bottom:%#;", [formatter stringFromNumber: number]];
You can use an NSNumberFormatter for this:
static NSNumberFormatter *numberFormatter = nil;
if (numberFormatter == nil) {
numberFormatter = [[NSNumberFormatter alloc] init];
numberFormatter.minimumFractionDigits = 0;
numberFormatter.maximumFractionDigits = 2;
numberFormatter.usesGroupingSeparator = NO;
}
NSString *formattedNumberString = [numberFormatter
stringForNumber:[NSNumber numberWithDouble: _paragraphSpacing]];
You can use C function modff to get the fraction part and test it:
float fractionPart = 0.;
modff(_paragraphSpacing, &fractionPart);
if( fabsf(fractionPart) < 0.01 ) {
// format as integer
[retString appendFormat:#"margin-bottom:%d", (int)_paragraphSpacing];
} else {
// format as float
[retString appendFormat:#"margin-bottom:%0.2f", _paragraphSpacing];
}

NSNumberFormatter to show a maximum of 3 digits

I would like to make it so one of my UILabels only shows a maximum of 3 digits. So, if the associated value is 3.224789, I'd like to see 3.22. If the value is 12.563, I'd like to see 12.5 and if the value is 298.38912 then I'd like to see 298. I've been trying to use NSNumberFormatter to accomplish this, but when I set the maximum significant digits to 3 it always has 3 digits after the decimal place. Here's the code:
NSNumberFormatter *distanceFormatter = [[[NSNumberFormatter alloc] init] autorelease];
[distanceFormatter setNumberStyle:NSNumberFormatterDecimalStyle];
[distanceFormatter setAlwaysShowsDecimalSeparator:NO];
[distanceFormatter setMaximumSignificantDigits:3];
I always thought 'significant digits' meant all the digits, before and after the decimal point. Anyway, is there a way of accomplishing this using NSNumberFormatter?
Thanks!
I believe that
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
formatter.usesSignificantDigits = YES;
formatter.maximumSignificantDigits = 3;
will do exactly what you want, i.e. it will always show exactly 3 digits, be it 2 before the decimal and 1 after or 1 before the decimal and 2 after.
Perhaps you also have to set (not sure though):
[distanceFormatter setUsesSignificantDigits: YES];
But in your case it's probably much easier to just use the standard string formatting:
CGFloat yourNumber = ...;
NSString* stringValue = [NSString stringWithFormat: #"%.3f", yourNumber];
(note: this will round the last digit)
Heres a small function I wrote:
int nDigits(double n)
{
n = fabs(n);
int nDigits = 0;
while (n > 1) {
n /= 10;
nDigits++;
}
return nDigits;
}
NSString *formatNumber(NSNumber *number, int sigFigures)
{
double num = [number doubleValue];
int numDigits = nDigits(num);
NSString *format = [NSString stringWithFormat:#"%%%i.%ilf", sigFigures -= numDigits, sigFigures];
return [NSString stringWithFormat:format, num];
}
In my tests, this worked fine.

Formatting a string containing a number with comma separation

I have a number stored in an NSMutableString instance which I want to auto format with comma delimiters and then display the result in a UITextField.
I've tried using NSNumberFormatter to format as currency, but I don't want it to show decimals if the original NSMutableString doesn't contain a decimal place.
For example:
If the NSMutableString contains "1234567", it should format as "1,234,567".
If the NSMutableString contains "1234567.1", it should format as "1,234,567.1"
If the NSMutableString contains "1234567.12", it should format as "1,234,567.12"
The maximum decimals that the NSMutableString will contain is 2.
Any help is greatly appreciated.
Thanks!
Keep in mind that you should really be localizing this if you are interacting with users on this, however here is one way to do it:
- (NSString *)formatString:(NSString *)string {
// Strip out the commas that may already be here:
NSString *newString = [string stringByReplacingOccurrencesOfString:#"," withString:#""];
if ([newString length] == 0) {
return nil;
}
// Check for illegal characters
NSCharacterSet *disallowedCharacters = [[NSCharacterSet characterSetWithCharactersInString:#"0123456789."] invertedSet];
NSRange charRange = [newString rangeOfCharacterFromSet:disallowedCharacters];
if ( charRange.location != NSNotFound) {
return nil;
}
// Split the string into the integer and decimal portions
NSArray *numberArray = [newString componentsSeparatedByString:#"."];
if ([numberArray count] > 2) {
// There is more than one decimal point
return nil;
}
// Get the integer
NSString *integer = [numberArray objectAtIndex:0];
NSUInteger integerDigits = [integer length];
if (integerDigits == 0) {
return nil;
}
// Format the integer.
// You can do this by first converting to a number and then back to a string,
// but I would rather keep it as a string instead of doing the double conversion.
// If performance is critical, I would convert this to a C string to do the formatting.
NSMutableString *formattedString = [[NSMutableString alloc] init];
if (integerDigits < 4) {
[formattedString appendString:integer];
} else {
// integer is 4 or more digits
NSUInteger startingDigits = integerDigits % 3;
if (startingDigits == 0) {
startingDigits = 3;
}
[formattedString setString:[integer substringToIndex:startingDigits]];
for (NSUInteger index = startingDigits; index < integerDigits; index = index + 3) {
[formattedString appendFormat:#",%#", [integer substringWithRange:NSMakeRange(index, 3)]];
}
}
// Add the decimal portion if there
if ([numberArray count] == 2) {
[formattedString appendString:#"."];
NSString *decimal = [numberArray objectAtIndex:1];
if ([decimal length] > 0) {
[formattedString appendString:decimal];
}
}
return formattedString;
}
// Test cases:
NSLog(#"%#", [self formatString:#"123456"]);
NSLog(#"%#", [self formatString:#"1234567."]);
NSLog(#"%#", [self formatString:#"12345678.1"]);
NSLog(#"%#", [self formatString:#"123456789.12"]);
// Output:
123,456
1,234,567.
12,345,678.1
123,456,789.12
I think this should do it -- I added an if statement to check if there is a decimal point in the typed in value. "Output" in this example is a property that I have bound to the value of a text field to show the result.
-(IBAction)doConversion:(id)sender{
NSNumberFormatter *formatter = [[NSNumberFormatter alloc]init];
[formatter setMaximumFractionDigits:2];
[formatter setUsesGroupingSeparator:YES];
[formatter setNumberStyle:NSNumberFormatterDecimalStyle];
double entryFieldFloat = [entryField doubleValue];
if ([entryField.stringValue rangeOfString:#"."].length == 1) {
formatter.alwaysShowsDecimalSeparator = YES;
self.output =[formatter stringFromNumber:[NSNumber numberWithDouble:entryFieldFloat]];
}else{
formatter.alwaysShowsDecimalSeparator = NO;
self.output =[formatter stringFromNumber:[NSNumber numberWithDouble:entryFieldFloat]];
}
}
Just call this method to be simple:
public static String GetCommaSeparatedCount(this Int32 Count)
{
// Check for The less-than character (<) is converted to <
String result = String.Format("{0:#,##0}", Count);
return result;
}
You're looking for the -setMinimumFractionDigits: method on NSNumberFormatter. Set that to 0 and it'll only display the decimal point if there's anything to put after it.

Checking input value

I am trying to check whether the user gives an input that is number but not letters. When a non-numeric value is given I want to print an alert error message like "incorrect format".
This is my source code:
-(IBAction)btnPressed{
NSString *firstString = textFiled1.text;
NSString *secondString = textFiled2.text;
NSString *thirdString = textFiled3.text;
int num1;
int num2;
int num3;
int output;
num1 = [firstString intValue];
num2 = [secondString intValue];
num3 = [thirdString intValue];
output = (num1 + num2) / num3;
lable1.text = [NSString stringWithFormat:#"%d",output];
}
Use NSNumberFormatter. If the input parameter is not a valid number, the number derived will be nil.
NSNumberFormatter * f = [[NSNumberFormatter alloc] init];
[f setNumberStyle:NSNumberFormatterDecimalStyle];
num1 = [f numberFromString:firstString];
[f release];
if (num1 == nil) {
// throw exception
}
This is how I would do it:
NSCharacterSet *nonNumbers = [[NSCharacterSet decimalDigitCharacterSet] invertedSet];
if ([firstString rangeOfCharacterFromSet:nonNumbers].location != NSNotFound) {
// firstString has non-number characters in it!
}

Convert string fraction to decimal

I have a plist file that I am reading out a measurement, but some of the measurements are fractions such as 6 3/8". I formatted them that way because it's easier to find that on a tape measure than it is to find 6.375". My problem is now I want to do a conversion to metric on the fly and it isn't reading in the fraction part of the number. My current code is this.
cutoutLabel.text = [NSString stringWithFormat:#"%.2f mm. %#", [[[sizeDict valueForKey:Sub_Size] objectForKey:#"Cutout Dimensions"]floatValue] * 25.4, [temp objectAtIndex:2]];
Thanks.
That's what I ended up doing.
NSArray *temp = [[[sizeDict valueForKey:Sub_Size] objectForKey:#"Cutout Dimensions"] componentsSeparatedByString:#" "];
if ([temp count] > 2) {
NSArray *fraction = [[temp objectAtIndex:1]componentsSeparatedByString:#"/"];
convertedFraction = [[fraction objectAtIndex:0]floatValue]/[[fraction objectAtIndex:1]floatValue];
}
You can get the numerator and denominator as follows:
NSRange slashPos = [fraction.text rangeOfString:#"/"];
NSString * numerator = [fraction.text substringToIndex:slashPos.location];
NSString * denominator = [fraction.text substringFromIndex:slashPos.location+1];
You should take more care than this,
check that your range is of length 1 and make sure that the string has characters after the "/" character. But if you know you are feeding this code a fraction string it should work in your case
The idea is in place, but you will also need to apply the same logic first to separate the whole number from you fraction. Apply the same logic, searching for a #" " and then find the numerator and denominator
Building on Ian's answer and trying to be a little more complete (since his example was for a whole number and fractional part with an inch character (6 3/8"), I suggest the following method (it also works if there are spaces before the whole number:
// Convert a string consisting of a whole and fractional value into a decimal number
-(float) getFloatValueFromString: (NSString *) stringValue {
// The input string value has a format similar to 2 1/4". Need to remove the inch (") character and
// everything after it.
NSRange found = [stringValue rangeOfString: #"\""]; // look for the occurrence of the " character
if (found.location != NSNotFound) {
// There is a " character. Get the substring up to the "\"" character
stringValue = [stringValue substringToIndex: found.location];
}
// Now the input format looks something like 2 1/4. Need to convert this to a float value
NSArray *temp = [stringValue componentsSeparatedByString:#" "];
float convertedFraction = 0;
float wholeNumber = 0;
for (int i=0; i<[temp count]; i++) {
if ([[temp objectAtIndex:i] isEqualToString:#""]) {
continue;
}
NSArray *fraction = [[temp objectAtIndex:i]componentsSeparatedByString:#"/"];
if ([fraction count] > 1) {
convertedFraction = [[fraction objectAtIndex:0]floatValue]/[[fraction objectAtIndex:1]floatValue];
}
else if ([fraction count] == 1) {
wholeNumber = [[fraction objectAtIndex:0] floatValue];
}
}
convertedFraction += wholeNumber;
return convertedFraction;
}