Swift中使用NumberFormatter格式化大十进制数字



我这样做是为了格式化数字,但对于大数字来说失败了

let formatter = NumberFormatter()
formatter.numberStyle = .decimal

if let number = formatter.number(from: "123456789123456789123") , let str = formatter.string(from:number){
print(number)
print(str)
}

它打印

123456789123456800000
123,456,789,123,456,800,000

它应该打印

123456789123456789123
123,456,789,123,456,789,123

我认为应该有数字溢出,有没有其他方法可以实现这种事情。

这个问题的根源有三个:

  • 从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.34569216,因为1234567890.12345678901234 567890是双精度文字,精度限制在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无法生成字符串1234567890.12345678901234567890,即使Decimal可以表示该精确值。

您可以使用字符串(描述:(获得小数的非本地化字符串表示

print (String(describing:(Decimal(string: "1234567890.12345678901234567890")!)))
// 1234567890.1234567890123456789

对于我自己的一个应用程序,我做了一个小";穷人的";十进制格式化程序,它只是使用所需Locale:的十进制分隔符输出完整值

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

您可以显式创建一个Decimal来解决上述错误

let formatter = NumberFormatter()
formatter.numberStyle = .decimal
if let decimalNumber = Decimal(string: "123456789123456789123"), let str = formatter.string(from:decimalNumber as NSNumber) {
print(decimalNumber)
print(str)
}

最新更新