这个问题的根源在于三个方面:
- 从 5.6 版开始,Swift 不支持小数文字。
- Decimal() 最多支持 38 位精度。
- Double() 支持大约 17 位精度。
因此,当使用 Decimal 表示精度超过 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:) 获得 Decimals 的非本地化字符串表示形式
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