When using a NumberFormatter and setting numberStyle to .currency, the formatter rounds all numbers to 2 decimal places, as expected. However, for small negative numbers, whose value rounded to 2 decimal places is 0 (number >= -0.005), the output string contains the negative sign and hence becomes -$0.00 instead of $0.00.

Is there are way to change this behaviour just by toggling some properties of the NumberFormatter instead of having to use a workaround? At the moment I am rounding the value to 2 decimal places myself, checking if the output is -0 and acting accordingly. However, it would be great if the NumberFormatter could be set up such that it doesn't distinguish -0 and 0.

Below code shows the issue and my current workaround (feel free to test in a playground) - I know that the Locale should be set based on the currencySymbol, but this is just code for showcasing the issue, not production code):

public class CurrencyFormatter {

    private let formatter: NumberFormatter

    public init(locale: Locale = Locale.current) {
        let formatter = NumberFormatter()
        formatter.locale = locale
        formatter.numberStyle = .currency
        self.formatter = formatter
    }

    // Adds currency symbol and returns a string e.g. input 1 output "£1"
    public func formatWithCurrencySymbol(value: Decimal, currencyCode: String) -> String? {
        formatter.currencyCode = currencyCode

        // Workaround for cases when the currency behaviour rounds small negative values (<0.0051) to -0.00, where we don't want to have a - sign
        var value = value
        if value < 0 {
            let roundedValue = round(Double(truncating: value as NSDecimalNumber) * 100.0)
            if roundedValue == 0 && roundedValue.sign == .minus {
                value = 0
            }
        }

        return formatter.string(for: value)
    }

    public func formatWithCurrencySymbolNoWorkaround(value: Decimal, currencyCode: String) -> String? {
        formatter.currencyCode = currencyCode
        return formatter.string(for: value)
    }
}

let formatter = CurrencyFormatter()

formatter.formatWithCurrencySymbol(value: 0.01, currencyCode: "USD") // "$0.01"
formatter.formatWithCurrencySymbol(value: -0.001, currencyCode: "EUR") // "€0.00"
formatter.formatWithCurrencySymbol(value: -0.0002, currencyCode: "EUR") // "€0.00"
formatter.formatWithCurrencySymbol(value: -0.01, currencyCode: "EUR") // "-€0.01"

formatter.formatWithCurrencySymbol(value: 0.01, currencyCode: "USD") // "$0.01"
formatter.formatWithCurrencySymbol(value: -0.001, currencyCode: "EUR") // "-€0.00"
formatter.formatWithCurrencySymbol(value: -0.0002, currencyCode: "EUR") // "-€0.00"
formatter.formatWithCurrencySymbol(value: -0.01, currencyCode: "EUR") // "-€0.01"
formatter.formatWithCurrencySymbolNoWorkaround(value: -0.005, currencyCode: "EUR") // "-€0.00"

0 Answers