这个问题的根源有三个方面:
- 从版本 5.6 开始,Swift 不支持十进制文字。
- Decimal() 支持最多 38 位精度。
- Double() 支持大约 17 位精度。
因此,当使用 Decimals 表示精度超过 17 位的数字时,切勿执行任何将 Decimal 值转换为 Double 的操作,这一点至关重要。这包括用文字初始化:
print (Decimal(1234567890.12345678901234567890))
// 1234567890.1234569216
print (Decimal(string: "1234567890.12345678901234567890")!)
// 1234567890.12345678901234567890
Decimal(1234567890.12345678901234567890) 生成值 1234567890.1234569216,因为 1234567890.12345678901234567890 是 Double 文字,精度限制为大约 17 位。
遗憾的是,NumberFormatter 显然从未收到有关此问题的备忘录。
var dec = Decimal(string: "1234567890.12345678901234567890")!
let f = NumberFormatter()
f.numberStyle = .decimal
f.minimumFractionDigits = 20
print (f.string(from: dec as NSDecimalNumber)!) // 1,234,567,890.12346000000000000000
因此,目前还没有办法让 Swift 准确地生成精度超过 17 位的本地化十进制数字。例如,NumberFormatter 无法生成字符串 1,234,567,890.12345678901234567890,即使 Decimal 可以表示该精确值。
您可以使用 String(describing:) 获取小数的非本地化字符串表示形式
print (String(describing:(Decimal(string: "1234567890.12345678901234567890")!)))
// 1234567890.1234567890123456789
对于我自己的一个应用程序,我制作了一个小小的“穷人”十进制格式化程序,它只是使用所需区域设置中的小数分隔符输出完整值:
let locale = Locale(identifier: "fr_FR")
dec = Decimal(string: "1234567890,12345678901234567890", locale: locale)!
let formatter = NumberFormatter()
formatter.locale = locale
let localizedSeparator = String(formatter.decimalSeparator.first ?? ".")
let fractionDigits = 20
var strings = String(describing:(dec)).split(separator: (Locale.current.decimalSeparator?.first) ?? "." )
var result = strings[0]
if fractionDigits > 0 {
if strings.count > 1 {
while strings[1].count < fractionDigits { strings[1] += "0" }
result += localizedSeparator + String(strings[1].prefix(fractionDigits))
}
else {
result += localizedSeparator + (0..<fractionDigits).map{_ in "0"}
}
}
print (result) // 1234567890,12345678901234567890