16

I want to use a number formatter to generate my output, so the number is automatically formatted for the user's locale, but I want it to work like "%+.1f" does in printf(), that is always have a sign specified.

NSNumberFormatter *nf = [[NSNumberFormatter alloc] init];
nf.numberStyle = NSNumberFormatterDecimalStyle;
nf.maximumFractionDigits = 1;

double val = 3.1234;
label.text = [NSString stringWithFormat: @"XXX %@ XXX", [nf stringFromNumber: [NSNumber numberWithDouble: val]]];

I want the label to come out "XXX +3.1 XXX" in the US and the appropriate but equivalent string for any other location. The only things I can find are setPositiveFormat: and setPositivePrefix:.

But I don't want to set the format since I don't know how to format numbers in other countries; I don't know if a plus-sign is used to designate a positive number in Arabic or Russian or some culture I have not thought of. I do know, for example, that decimal points, commas, spaces, etc., all have different meanings in European countries compared to the U.S. - Could the same be true for +/- signs?

What I do currently is:

label.text = [NSString stringWithFormat: @"XXX %s%@ XXX", (val < 0) ? "" : "+",
    [nf stringFromNumber: [NSNumber numberWithDouble: val]]];

But this presumes that '+' and '-' are correct for all formats.

I'm sure it must be there since it is a standard formatting thing that has been in printf() since the dark ages...

LavaSlider
  • 2,494
  • 18
  • 29

7 Answers7

21

How about this:

NSNumberFormatter *nf = [[NSNumberFormatter alloc] init];
nf.numberStyle = NSNumberFormatterDecimalStyle;
nf.maximumFractionDigits = 1;

double val = 3.1234;
NSString *sign = (val < 0) ? [nf minusSign] : [nf plusSign];
NSString *num = [nf stringFromNumber:@(abs(val))]; // avoid double negative
label.text = [NSString stringWithFormat: @"XXX %@%@ XXX", sign, num];

You may need to check to see if num has the sign prefix or not so it isn't shown twice.

Edit: After some playing around, it has been determined, for the "Decimal" style, that no current locale uses a positivePrefix. No current locale uses a plusSign other than the standard + character. No current locale uses a negativePrefix that is different than minusSign. No current locale uses either positiveSuffix or negativeSuffix.

So an easier approach would be to do:

NSNumberFormatter *nf = [[NSNumberFormatter alloc] init];
nf.numberStyle = NSNumberFormatterDecimalStyle;
nf.maximumFractionDigits = 1;
[nf setPositivePrefix:[nf plusSign]];
[nf setNegativePrefix:[nf minusSign]];

label.text = [nf stringFromNumber:@(val)];
rmaddy
  • 314,917
  • 42
  • 532
  • 579
  • Thanks... this may be as close as I can get without a built-in way to do it. It does assume that the plus or minus always goes in the front. I guess I'll wait for the feedback from some user that my number is not displayed correctly! – LavaSlider Jan 04 '13 at 04:36
  • You can do it with positivePrefix. – Ramy Al Zuhouri Jan 04 '13 at 10:38
  • Thanks @maddy, I like this solution. I made one addition. I store [nf positivePrefix] and [nf negativePrefix] before setting them to [nf plusSign] and [nf minusSign]. Then after using the formatter restore them since I re-use the number formatter other places without a forced sign. – LavaSlider Jan 04 '13 at 16:35
  • Actually, your claim about negative prefixes and suffixes is not true. Try it with currency format and you'll get completely different negative prefixes. – Sulthan Mar 10 '13 at 15:16
  • The claim was made for Decimal style, not Currency style. I'll make that clearer in the answer. – rmaddy Mar 10 '13 at 17:08
14

This case it's simple, just add the prefix:

nf.positivePrefix= nf.plusSign;
Ramy Al Zuhouri
  • 21,580
  • 26
  • 105
  • 187
3

Though it won't use the user's locale, you can do the following to generate the +/- sign without the somewhat expensive overhead of an NSNumberFormatter:

// assume 'number' is an NSNumber
label.text = [NSString stringWithFormat:@"%+.02f", [number floatValue]];
Brian Sachetta
  • 3,319
  • 2
  • 34
  • 45
3

Simple Case:

    let f = NumberFormatter()
    f.positivePrefix = f.plusSign

Currency Case :

Hack needed, because setting the prefix to plusSign only will remove the currency symbol.

    let f = NumberFormatter()
    f.numberStyle = .currency
    f.positivePrefix = f.plusSign + f.currencySymbol

There is a bit more work depending on the locale.. The currency symbol may be before, or after, but this is probably another subject..

Edit:

Even if it is another subject, I'd say a possible solution to the problem above is to subclass NSNumberFormatter :

override func string(from number: NSNumber) -> String? {
    returns ( number.doubleValue >= 0 ? super.plusSign : "" ) + super.string(from: number)
}

This way, NSNumberFormatter should manage the currency position while your subclass simply prepend the + sign. No time to test this in depth, but at least it is an approach.

Moose
  • 2,607
  • 24
  • 23
0

The underlying formatting language for NSNumberFormatter doesn't have any provision for what you want to do -- it will allow you to specify a localized positive sign on exponents, but not for the entire formatted string. Nor does NSLocale seem to make available the localized positive sign.

Aside from making a dummy string that includes an exponent, pulling the localized positive sign out, and putting your final formatted string together by hand, I think you're out of luck.

jscs
  • 63,694
  • 13
  • 151
  • 195
  • 4
    You can get the localized plus prefix from `NSNumberFormatter plusSign`. – rmaddy Jan 04 '13 at 02:59
  • Is there any locale that would use a different symbol for the plus sign? – omz Jan 04 '13 at 04:33
  • 1
    @omz I just ran a quick test - every locale (from `NSLocale availableLocaleIdentifiers`) gave a `+` for `NSNumberFormatter plusSign`. Most gave `-` (hyphen) for `minusSign`. A few used \u2212 (minus sign). – rmaddy Jan 04 '13 at 06:10
  • positivePrefix is what you're seeking for. – Ramy Al Zuhouri Jan 04 '13 at 10:38
0

A reusable formatter in swift:

var numberFormatter: NSNumberFormatter {
    let formatter = NSNumberFormatter()
    formatter.numberStyle = .DecimalStyle
    formatter.locale = NSLocale(localeIdentifier: "it_IT")//your Locale
    formatter.maximumFractionDigits = 2
    formatter.minimumFractionDigits = 0
    formatter.positivePrefix = formatter.plusSign
    return formatter
}

Then use it:

let myDoubleValue = 12.00
let myStringNumber = numberFormatter.stringFromNumber(myDoubleValue)!
lifeisfoo
  • 15,478
  • 6
  • 74
  • 115
0

I don't think any of the previous answers will actually take into consideration everything you mentioned in your question.

It is true that NumberFormatter does not have an option to set the plus sign visible for all positive numbers when formatting currency values.

Also, replacing prefixes and suffixes will likely break the format for some regions and always replacing a prefix will only work if the set locale uses the currency symbol on the left.

A simple way to address this without losing the locale formatting can be seen below:

var value: Double = 3.1234
let formatter = NumberFormatter()
formatter.numberStyle = .currency
formatter.maximumFractionDigits = 1

if value > 0 {
    return formatter.string(for: value.negated())?.replacingOccurrences(
            of: formatter.minusSign,
            with: formatter.plusSign
        )
} else {
    return formatter.string(for: value)
}

Even though this can be seen as hack, it's an effective way to achieve everything you mentioned without manually writing a number formatter.

Gabriel Gomes
  • 91
  • 1
  • 9