NumberFormatter 在货币模式下对小负数的错误行为

Posted

技术标签:

【中文标题】NumberFormatter 在货币模式下对小负数的错误行为【英文标题】:NumberFormatter incorrect behaviour for small negative numbers in currency mode 【发布时间】:2019-04-11 10:34:29 【问题描述】:

当使用 NumberFormatter 并将 numberStyle 设置为 .currency 时,格式化程序将所有数字四舍五入到小数点后 2 位,如预期的那样。但是,对于小负数,其值四舍五入到小数点后 2 位为 0(数字 >= -0.005),输出字符串包含负号,因此变为 -$0.00 而不是 $0.00

有没有办法通过切换NumberFormatter 的某些属性来改变这种行为,而不必使用解决方法?目前,我自己将值四舍五入到小数点后 2 位,检查输出是否为-0 并采取相应措施。但是,如果NumberFormatter 可以设置为不区分-00,那就太好了。

下面的代码显示了问题和我当前的解决方法(请随意在操场上进行测试) - 我知道 Locale 应该基于 currencySymbol 设置,但这只是展示问题的代码,而不是生产代码):

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"

【问题讨论】:

相关:NSNumberFormatter rounding to negative zero @vacawama 谢谢,看来确实没有合适的配置解决方案 似乎没有,这很遗憾,因为许多人会发现负零是一个荒谬的显示。这让每个人都想出一个解决方法。 【参考方案1】:

您可以添加简单的逻辑来检查该值是否小于 -0.01,如果是,则返回绝对值。

return value &lt; -0.01 ? formatter.string(for: abs(value)) : formatter.string(for: value)

【讨论】:

正如您在问题中看到的那样,我在询问之前已经找到了解决方法,但我并不是在寻找解决方法,我正在寻找使用 NumberFormatter 属性的适当解决方案来获取期望的行为(遗憾的是似乎不存在)。正如句子中所解释的那样:“有没有办法通过切换 NumberFormatter 的某些属性来改变这种行为,而不必使用解决方法?” 是的,很抱歉不得不使用完整的答案来传达我的想法。我会将此添加为评论,但我还没有足够的声誉点来直接评论。我只是提供了一个可能更简单的解决方法。如果 Apple 能够添加一个允许我们控制这种行为的属性,那就太好了。

以上是关于NumberFormatter 在货币模式下对小负数的错误行为的主要内容,如果未能解决你的问题,请参考以下文章

来自 NumberFormatter 的本地化货币符号

Swift3:类型 numberformatter 没有成员“货币样式”,swift3

SwiftUI TextField 中的货币输入

NumberFormatter 分组未按预期工作

使用 JSONSerialization 序列化货币时指定小数位数

NumberFormatter 在 Swift 中无法识别来自不同本地的数字 [关闭]