Prevent small negative numbers printing as "-0"
Asked Answered
C

5

5

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]];
Crockett answered 10/6, 2012 at 14:15 Comment(0)
T
7

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.

Twomey answered 18/11, 2013 at 13:26 Comment(1)
Thanks @fishinear, better late than never, I hope :-) – Twomey
S
1

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.

Sophocles answered 10/6, 2012 at 16:52 Comment(4)
Well, it is actually not for presenting to the user in this case. But besides that, NSNumberFormatter seems to have the same issue (see edit). Do you know what parameter to set on NSNumberFormatter to prevent this? – Crockett
Oops. I was hoping the formatter can check at least this (I am already using my own formatter for NSDecimalNumbers). See my edit. – Sophocles
So I guess there is no easy answer. I will accept your answer, because post-processing at least avoids knowing the exact internals of the operation. – Crockett
Just another thing that SHOULD be implemented in NSNumberFormatter. I switched to my own formatter long ago because other problems, anyway. – Sophocles
C
0
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];
Calicut answered 10/6, 2012 at 14:27 Comment(3)
Thanks for your answer, but I would only want values that result in zero to print without minus sign. For example, -0.1 should still result in @"-0.1". – Crockett
Thanks for the edit. That kind of approach will of course work, but is dependent on the precise workings of the internals of the stringWithFormat: method, and on the format string I am using. For example, your current approach produces an incorrect "0.1" for the value -0.05. I was hoping to avoid that kind of fine tuning to the internals. – Crockett
you'r rigth. As @sulthan said, 'NSNumberFormatter' is your solution – Calicut
E
0

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
Excurved answered 15/4, 2015 at 19:17 Comment(1)
floatValue will round off the decimal number as well. – Excurved
B
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) }
  }
}

enter image description here

Bricky answered 19/4, 2022 at 14:20 Comment(0)

© 2022 - 2024 β€” McMap. All rights reserved.