0

How to convert number formate with separated by ,

Example Resultant String

 "38963.26" value into "38,863.26"
  "1013321.22" Value into "10,13,321.22"

//MARK:- Seperator number formate 1000 - 1,000
extension Formatter {
    static let withSeparator: NumberFormatter = {
        let formatter = NumberFormatter()
        formatter.groupingSeparator = ","
        formatter.numberStyle = .decimal
        return formatter
    }()
}

extension BinaryFloatingPoint {
    var formattedWithSeparator: String {
        return Formatter.withSeparator.string(for: self) ?? ""
    }
}


let resultValue = StringValue.CGFloatValue()?.formattedWithSeparator
print("Resultant", resultValue!)

CGFloatVaule default method for converting String to floatValue. same like String.floatValue.

For value "38963.26" is gives resultant value "38,963.262" I wonder why its like that extra 2 in decimal.

print("38963.26".CGFloatValue()!.formattedWithSeparator)
Output "38,863.262"
kiran
  • 4,285
  • 7
  • 53
  • 98
  • What is `StringValue` ? – ielyamani May 04 '19 at 13:36
  • @ielyamani The String Value is "38963.26" – kiran May 04 '19 at 13:37
  • And what is `CGFloatValue`? Is it an extension you've defined for strings? Could you edit your question instead of responding in the comments? – ielyamani May 04 '19 at 13:38
  • CGFloatValue is used to convert String to floatValue. – kiran May 04 '19 at 13:54
  • 1
    Value indicator some country says 100 thousand and few other says 1 lakh. In my case its goes this way "10,13,321.22". – kiran May 04 '19 at 18:54
  • Excellent. By the way, be wary of `Float`/`CGFloat`. You’re currently worried about the third decimal place. Try a bigger number and the problem is no longer a negligible rounding error. Try a string like “123456789012345.67” . At the very least, consider `Double` (which moves the rounding error from the 7th significant decimal digit to the 15th). Better, consider using `Decimal`, with 38 digits. – Rob May 04 '19 at 19:31

3 Answers3

2

Either set a proper (that uses this style for formatting) locale or set both decimal and grouping separator on your formatter instance since not all Locale might use the same separators.

Then you also need to set max number of fraction digits if you are using float (for some reason this isn't needed for Double)

let formatter = NumberFormatter()
formatter.numberStyle = .decimal
formatter.locale = Locale(identifier: "en_US")
formatter.maximumFractionDigits = 2
if let str = formatter.string(for: Float(numberString)) {
    print(str)
}


let formatter = NumberFormatter()
formatter.groupingSeparator = ","
formatter.decimalSeparator = "."
formatter.numberStyle = .decimal
formatter.maximumFractionDigits = 2
if let str = formatter.string(for: Float(numberString)) {
    print(str)
}

Both yields

38,963.26

Joakim Danielson
  • 43,251
  • 5
  • 22
  • 52
  • Its Helps for my solution Joakim Danielson. But why the extra decimal value added while conversation Output "38,863.262" ? Any Idea have a comment below. – kiran May 04 '19 at 18:34
  • @kiran I don't know why Float behaves this way and as I wrote in the answer it works fine for Double, looks like some issue with the NumberFormatter class. – Joakim Danielson May 04 '19 at 18:55
  • That answer gets to the esoteric issue that certain (even small) decimal numbers simply can’t be reproduced perfectly in floating point numbers. But, this is a related (but, IMHO, simpler) issue, that he is simply exceeding the `significandBitCount` of `Float`. – Rob May 04 '19 at 19:53
0

Refer this:- how to add commas to a number in swift?enter link description here

let numberFormatter = NumberFormatter()
numberFormatter.numberStyle = .decimal

let numberString = "38963.26"
if let number = Double(numberString),
    let formattedNumber = numberFormatter.string(from: NSNumber(value: number)) {
        let formattedString = "\(formattedNumber)"
        print(formattedString)
    }
  • Instead of `string(from:)` and `NSNumber`, use `string(for:)` and use the `Double` directly, bypassing the `NSNumber`. Also, the result is not optional. – Rob May 04 '19 at 18:06
0

There are three issues:

  1. The use of explicit grouping separator.

    Forcing the thousands separator to “,” is counterproductive. If the device is in a locale where that’s already the default, you don’t need to specify it. And if the device is in a locale where it’s not default, it will be exceedingly confusing for a user who is not used to seeing “,” thousandths separators. And worse, if you were to override the grouping separator, you should also override the decimal separator, too. If this is for a string to be presented in the user interface, I would suggest not touching the grouping separator (or any of those properties):

    static let decimal: NumberFormatter = {
        let formatter = NumberFormatter()
        formatter.numberStyle = .decimal
        return formatter
    }()
    

    If you really want to force use , for grouping separator and . for decimal separator (even though the user might be in a locale where that might be exceeding confusing were you to present that in the user interface), you should go ahead and change the locale to whatever you want, e.g.

    static let decimal: NumberFormatter = {
        let formatter = NumberFormatter()
        formatter.numberStyle = .decimal
        formatter.locale = Locale(identifier: "en_US") // or "en_IN" or whatever
        return formatter
    }()
    
  2. Decimal places.

    If you always want two decimal places, then:

    static let decimal: NumberFormatter = {
        let formatter = NumberFormatter()
        formatter.numberStyle = .decimal
        formatter.minimumFractionDigits = 2
        formatter.maximumFractionDigits = 2
        return formatter
    }()
    

    By setting minimum fraction digits, you’re ensured that 42.1 will show up as “42.10”. By setting maximum fraction digits, you’re ensured that 42.33333 will show up as “42.33”.

  3. Issues resulting from conversion of string to binary floating point and back to string.

    The fundamental problem with the decimal places is that you’re converting a string to a binary floating point number. If you use Double, you’ll capture more significant digits than you will with Float/CGFloat, but you’re still introducing rounding errors as you convert a string to a binary floating point representation. Float starts introducing conversion errors at the 7th significant decimal digit. Double defers the conversion errors to the 15th significant decimal digit.

    You might consider using a numeric format that will capture up to 38 digits without rounding issues, namely Decimal. Thus:

    extension NumberFormatter {
        static let decimal: NumberFormatter = {
            let formatter = NumberFormatter()
            formatter.numberStyle = .decimal
            // specify locale and/or fraction digits as needed
            return formatter
        }()
    }
    
    extension String {
        func decimalFormatted() -> String? {
            return Decimal(string: self).flatMap { NumberFormatter.decimal.string(for: $0) }
        }
    }
    

    And then

    let string = "38963.26"
    let foo = string.decimalFormatted() ?? ""
    
Rob
  • 415,655
  • 72
  • 787
  • 1,044