'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]];


Solution 1:[1]

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.

Solution 2:[2]

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.

Solution 3:[3]

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];

Solution 4:[4]

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

Solution 5:[5]

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) }
  }
}

enter image description here

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Community
Solution 2
Solution 3
Solution 4 ArunGJ
Solution 5 ober