Swift 在十进制格式中失去精度

OMG*_*POP 3 swift

使用小数类型处理货币输入时,我遇到精度问题。问题出在格式化程序上。这是游乐场中最小的可重现代码:

let formatter = NumberFormatter()
formatter.numberStyle = .currency
formatter.isLenient = true
formatter.maximumFractionDigits = 2
formatter.generatesDecimalNumbers = true

let text = "89806.9"
let decimal = formatter.number(from: text)?.decimalValue ?? .zero
let string = "\(decimal)"
print(string)
Run Code Online (Sandbox Code Playgroud)

它打印出来89806.89999999999而不是89806.9. 然而,大多数其他数字都很好(例如8980.9)。所以我不认为这是双精度与小数的问题。

编辑:

我需要使用格式化程序的原因是有时我需要处理货币格式输入:

let text = "$89,806.9"
let decimal = formatter.number(from: text)?.decimalValue ?? .zero
print("\(decimal)") // prints 89806.89999999999

let text2 = "$89,806.9"
let decimal2 = Decimal(string: text2)
print("\(decimal2)") // prints nil

Run Code Online (Sandbox Code Playgroud)

Joa*_*son 5

使用新的 FormatStyle 似乎会生成正确的结果

\n
let format = Decimal.FormatStyle\n    .number\n    .precision(.fractionLength(0...2))\n\n\nlet text = "89806.9"\nlet value = try! format.parseStrategy.parse(text)\n
Run Code Online (Sandbox Code Playgroud)\n

下面是使用区域设置中的货币代码解析货币的示例

\n
let currencyFormat = Decimal.FormatStyle.Currency\n    .currency(code: Locale.current.currencyCode!)\n    .precision(.fractionLength(0...2))\n\nlet amount = try! currencyFormat.parseStrategy.parse(text)\n
Run Code Online (Sandbox Code Playgroud)\n

瑞典语示例:

\n
let text = "89806,9 kr"\nprint(amount)\n
Run Code Online (Sandbox Code Playgroud)\n
\n

89806.9

\n
\n

另一种选择是使用 Decimal 的新 init,它采用 String 和 FormatStyle.Currency (或数字或百分比)

\n
let amount = try Decimal(text, format: currencyFormat)\n
Run Code Online (Sandbox Code Playgroud)\n

为了格式化这个值,我们可以使用formatted(_:)Decimal

\n
print(amount.formatted(currencyFormat))\n
Run Code Online (Sandbox Code Playgroud)\n

输出(仍然是瑞典语):

\n
\n

89\xc2\xa0806,9\xc2\xa0kr

\n
\n