iosswiftnsstringnsnumbernsnumberformatter

NumberFormatter wrong result for 0.9972


I have a double:

let value = 0.99720317490866084

And a Double extention function:

extension Double {
    func stringWithFixedFractionDigits(min: Int, max: Int) -> String {
        let formatter = NumberFormatter()
        formatter.minimumFractionDigits = min
        formatter.maximumFractionDigits = max
        formatter.minimumIntegerDigits = 1
        let numberObject = NSNumber(value: self)
        return formatter.string(from: numberObject) ?? "\(self)"
    }
}

If I use:

value.stringWithFixedFractionDigits(min: 2, max: 2)

I get 1.00 but I would like to get 0.99

What can I change?


Solution

  • You just need to set your NumberFormatter rounding mode property to .down:

    formatter.roundingMode = .down
    

    Note that you don't need to create a new NSNumber object, you can safely cast from Double to NSNumber or use string(for: Any) method instead. As an alternative you can extend the protocol FloatingPoint to make it available to all Float types :

    extension Formatter {
        static let number = NumberFormatter()
    }
    extension FloatingPoint {
        func formattedWithFractionDigits(minimum: Int = 2, maximum: Int = 2, minimumIntegerDigits: Int = 1, roundingMode: NumberFormatter.RoundingMode = .halfEven) -> String {
            Formatter.number.roundingMode = roundingMode
            Formatter.number.minimumFractionDigits = minimum
            Formatter.number.maximumFractionDigits = maximum
            Formatter.number.minimumIntegerDigits = minimumIntegerDigits
            return Formatter.number.string(for: self) ?? ""
        }
    }
    
    
    0.9972031749.formattedWithFractionDigits()                                              // 1.00
    0.9972031749.formattedWithFractionDigits(roundingMode: .down)                           // "0.99"
    0.9972031749.formattedWithFractionDigits(minimumIntegerDigits: 0, roundingMode: .down)  // ".99"