【问题标题】:Large decimal number formatting using NumberFormatter in Swift在 Swift 中使用 NumberFormatter 格式化大十进制数
【发布时间】:2020-03-22 10:07:11
【问题描述】:

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

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

我觉得应该是数字溢出,有没有办法实现这种事情。

【问题讨论】:

  • 您必须设置formatter.generatesDecimalNumbers = true,但由于NumberFormatter 中的错误,即使这样也不起作用,比较stackoverflow.com/q/43538661/1187415,它也有解决方法。
  • 这里根本不使用小数部分,只使用数字样式作为小数
  • 如果没有建议的选项,您可以使用二进制浮点类型 Double 创建一个 Number 来表示其内部表示。 Double 的精度限制在大约。 17 位十进制数字。
  • @MartinR 你知道格式化这么大数字的其他方法吗?

标签: ios swift xcode largenumber numberformatter


【解决方案1】:

您可以显式创建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)
}

【讨论】:

  • NumberFormatter 不能准确地为所有大的 Decimal() 值生成字符串。
【解决方案2】:

这个问题的根源在于三个方面:

  • 从 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

【讨论】:

    猜你喜欢
    • 2017-10-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-22
    • 1970-01-01
    • 2013-07-29
    相关资源
    最近更新 更多