在 Swift 中使用 NumberFormatter 格式化大十进制数
Large decimal number formatting using NumberFormatter in Swift
我这样做是为了格式化数字,但是对于大数字它会失败
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
我觉得应该是数字溢出了,请问有什么办法可以实现这种事情吗
您可以创建一个 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)
}
这个问题的根源是three-fold:
- 从版本 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:)non-localized 获得 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
我这样做是为了格式化数字,但是对于大数字它会失败
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
我觉得应该是数字溢出了,请问有什么办法可以实现这种事情吗
您可以创建一个 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)
}
这个问题的根源是three-fold:
- 从版本 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:)non-localized 获得 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