skip to Main Content

I’ve developed a custom control for money input, which contains UITextField and UILabel. When the user taps on it, it becomes active and switches to the UITextField for data input and accepts only numbers and dot symbol, when the user finishes editing it becomes passive and switches to UILabel just to show formatted money value. But there is one little issue which I’m unable to fix a lot of days already.

Let’s say the user writes down 88.99 and presses done, this becomes "$ 88.99" in a UILabel, next when the user again taps on it to edit the initial value I get the following value "88.98999999999999". To not present the entire code I selected the core part in a playground format which gives the same result as in my complete project:

extension NumberFormatter {
    static public func defaultCurrencyFormatter() -> NumberFormatter {
        let formatter = NumberFormatter()
        formatter.usesGroupingSeparator = true
        formatter.numberStyle = .currency
        formatter.currencySymbol = ""
        formatter.minimumFractionDigits = 1
        formatter.maximumFractionDigits = 2
        formatter.currencyGroupingSeparator = ","
        formatter.currencyDecimalSeparator = "."
        return formatter
    }
}

let stringValue = NumberFormatter.defaultCurrencyFormatter().number(from: "88.99")?.stringValue

print(stringValue) // result is Optional("88.98999999999999")

I have no idea why using this NumberFormatter I get such a result. I was thinking that explicitly setting minimumFractionDigits and maximumFractionDigits will solve my issue but it does not affect my result

2

Answers


  1. NumberFormatter is legacy from objc and it operates with NSNumber/CGFloat etc. and usually it is helpful for localized text formatting. Much powerful and convenient parser for numbers is Scanner but if you don’t have complex data structure to parse and don’t want to deal with Floating-point error mitigation just use swift’s Float:

    // Float from string
    if let float = Float("88.99") {
        print(float)
        
        // String from float
        let text = String(float)
        print(text)
    }
    
    Prints:
    
    88.99
    88.99
    
    Login or Signup to reply.
  2. Try this:

    extension String {
        var currencyStyle: String? {
            let formatter = NumberFormatter()
            formatter.minimumFractionDigits = 1
            formatter.maximumFractionDigits = 2
            formatter.usesGroupingSeparator = true
            formatter.groupingSize = 3
            formatter.currencyGroupingSeparator = ","
            formatter.currencyDecimalSeparator = "."
    
            if let double = Double(self) {
                let number = NSNumber(value: double)
                return formatter.string(from: number)
            }
            return nil
        }
    }
    

    to use it:

        let str = "12388.98999999999999".currencyStyle
        print(str) // Optional("12,388.99")
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search