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.