5

Is there a quick way to format numbers (e.g., when using appendFormat) so that only enough decimal places are shown? E.g., 4.23100 is shown as 4.231, while 5.0000 is shown as just 5.

Thanks for reading.

Rogare
  • 3,234
  • 3
  • 27
  • 50
  • 5
    This doesn't make sense since floating-point representation ain't exact. **You** specify the decimal places needed. –  Nov 03 '12 at 21:21
  • 1
    Thanks for the comment. Hopefully this clarifies things: If I had two variables, say x=4.231 and y=5, and I want to make a string with them, like @"%f+%f", I'd end up with "4.231000 + 5.000000". However, I need "4.231 + 5" and I'm wondering if there's any built in functionality to do this, or whether I just code the function myself. – Rogare Nov 03 '12 at 21:24
  • 3
    @Rogare After `x` is initialized its value is `4.2309999999999998721023075631819665431976318359375`. Now how should the compiler decide how many digits you want? – Pascal Cuoq Nov 03 '12 at 21:25
  • 1
    How about this: you want to convert your number to decimal (string) with just enough digits that, if the string is converted back to binary, the same floating-point number is obtained again. Does this sound like what you are asking? (if this were C, the functions involved would be `sprintf` and `strtod`). Note that if the numbers involved are the results of floating-point computations, instead of just constants `x=4.231`, for the criteria I suggest, you may get up to 17 significant digits (say, `7.000000000000002` instead of `7.`. This is just how floating-point works. – Pascal Cuoq Nov 03 '12 at 21:36
  • Try `%g` as opposed to `%f`. Scientific notation, and removes unwanted decimal places. – msgambel Nov 03 '12 at 22:35
  • possible duplicate of [Correcting floating point numbers](http://stackoverflow.com/questions/10049533/correcting-floating-point-numbers) – lnafziger Nov 03 '12 at 23:07
  • See this thread... http://stackoverflow.com/questions/7271560/correct-use-of-format-specifier-to-show-up-to-three-decimals-if-needed-otherwis David – David Nedrow Nov 04 '12 at 04:12
  • Thanks for the links, I had written my own code in the meantime, but the NSNumberFormatter class was exactly what I was looking for. Thanks! – Rogare Nov 04 '12 at 12:53
  • If you store the original values as strings instead of floating-point numbers, the question makes a lot of sense. See my answer below for how I handle this in a similar situation. – paulmelnikow Dec 27 '12 at 05:05

4 Answers4

4

Use %g instead of %f

double n = 1234.5678;

NSString str = [NSString stringWithFormat:@"number is %g", n];

Roger Gilbrat
  • 3,755
  • 5
  • 34
  • 58
  • 1
    Hi Roger, thanks for the answer! I tried this code and it actually returns 1234.56. That said, I believe if I use the NSNumberFormatter class (e.g., http://stackoverflow.com/questions/7271560/correct-use-of-format-specifier-to-show-up-to-three-decimals-if-needed-otherwis) and increase the MaximumFractionDigits, it should work as needed. Thanks! – Rogare Nov 04 '12 at 13:03
  • As referenced [here](http://stackoverflow.com/a/5913115/1202222), the %g conversion will default to 6 significant figures unless a maximum is specified. %.8g or larger will all give the desired float. – Tim Windsor Brown Dec 10 '13 at 15:20
3

Use %g with the appropriate precision.

double number = 1234.123;

NSLog(@"Number equals: %.8g", number)

As referenced here, the %g conversion will default to 6 significant figures unless a maximum precision is specified. %.7g or larger will all give the desired float.

Community
  • 1
  • 1
Tim Windsor Brown
  • 4,069
  • 5
  • 25
  • 33
0

I couldn't find a built-in way to do this at first, so I built a quick function (below) if anyone is interested. That said, I believe %g (Thanks MSgambel, Roger Gilbrat) and the NSNumberFormatter class (Thanks Inafziger, David Nedrow) do this much more succinctly, so they're probably the better option!

+ (NSString *)trimmedFraction:(float)fraction{

    int i=0; 
    float numDiff=0; 

    // Loop through possible decimal values (in this case, maximum of 6)
    for (i=0;i<=6;i++){

        // Calculate difference between fraction and rounded fraction
        numDiff=round(fraction*pow(10,i))/pow(10,i)-fraction;

        // Check if difference is less than half of the smallest possible value
        if (fabsf(numDiff)<(0.5*pow(10,-6))){
            break;
        }
    }

    // Return string of truncated fraction
    NSString *stringPattern=[NSString stringWithFormat:@"%%0.%df",i];
    return [NSString stringWithFormat:stringPattern,fraction];
}

In the code you can specify the maximum number of decimal spaces (e.g., 6 in this case). Then you just send it a fraction and it returns a string with only the relevant decimals. For example, send it 4.231000 and it returns @"4.231", send it 5.000000 and it returns @"5". (Again, probably better to just use %g and NSNumberWithFormatter).

Rogare
  • 3,234
  • 3
  • 27
  • 50
0

If you store the original values as strings instead of floating-point numbers, the question makes a lot of sense.

I handle this using a category on NSString:

+ (NSString *) stringForNotANumber {
    static NSString *singleton;
    if (!singleton) singleton = @"NaN";
    return singleton;
}

- (NSUInteger) numberOfDecimalPlaces {
    NSString *strValue = self;

    // If nil, return -1
    if (!strValue) return -1;

    // If non-numeric, return -1
    NSNumberFormatter *f = [[NSNumberFormatter alloc] init];
    [f setMaximumFractionDigits:128];
    NSNumber *numValue = [f numberFromString:strValue];
    if (!numValue) return -1;

    // Count digits after decimal point in original input
    NSRange range = [strValue rangeOfString:@"."];
    if (NSNotFound == range.location) return 0;

    return [strValue substringFromIndex:range.location+1].length;
}

- (NSString *) plus:(NSString *) addend1 {
    NSString *addend2 = self;

    if (!addend1 || !addend2) return nil;
    if (addend1 == [NSString stringForNotANumber] ||
        addend2 == [NSString stringForNotANumber])
        return [NSString stringForNotANumber];

    NSNumberFormatter *f = [[NSNumberFormatter alloc] init];
    NSNumber *num1 = [f numberFromString:addend1];
    NSNumber *num2 = [f numberFromString:addend2];
    if (!num1 || !num2) return [NSString stringForNotANumber];

    double sum = num1.doubleValue + num2.doubleValue;

    [f setMinimumFractionDigits:MAX(addend1.numberOfDecimalPlaces,
                                    addend2.numberOfDecimalPlaces)];

    return [f stringFromNumber:[NSNumber numberWithDouble:sum]];
}
paulmelnikow
  • 16,895
  • 8
  • 63
  • 114