Exactly Parsing Decimals from Strings and Currency

This is an age old problem that should be solved by using NumberFormatter. Given a formatter:

extension NumberFormatter {
    /// Currency formatter for USD only.
    static let dollars: NumberFormatter = {
        let formatter = NumberFormatter()
        formatter.numberStyle = .currency
        formatter.maximumFractionDigits = 2
        formatter.locale = Locale(identifier: "en_US")

        return formatter
    }()
}

Parsing a string like $0.07 as such: NumberFormatter.dollars.number(from: string)!.decimalValue should result in an exact value. It doesn't. For some values where this fails, parsing the doubleValue may be more precise: Decimal(NumberFormatter.dollars.number(from: string)!.doubleValue). However, this fails for some values too. $0.07 fails both methods. Manually parsing gives exact results: Decimal(string: String(string.dropFirst().filter { $0 != "," })). However, this is extremely non-localizable and rather slow.

So what's the solution here? Is there a good one?

3 Likes

Did you set generatesDecimalNumbers on the formatter object? https://developer.apple.com/documentation/foundation/nsnumberformatter/1410503-generatesdecimalnumbers?language=objc

That setting seems to have no affect. $0.07 still fails to parse exactly.

NSNumberFormatter is the logical choice, and it should be fixed
http://www.openradar.me/29923468

4 Likes