iOS自定义金额输入控件数字精度异常:88.99转成88.98999999999999求助
解决金额输入控件中NumberFormatter导致的浮点数精度问题
这个问题其实是二进制浮点数的精度局限性在搞鬼,加上你当前用的NumberFormatter默认会把字符串转成Double类型,而Double无法精确表示像0.99这样的十进制小数,才会出现这种奇怪的近似值。
问题根源拆解
- 当你调用
number(from: "88.99")时,NumberFormatter默认返回的是Double类型的NSNumber。而十进制的88.99转换成二进制浮点数时,会变成一个无限循环的近似值——也就是你看到的88.98999999999999,这是浮点数的天生缺陷。 - 你设置的
minimumFractionDigits和maximumFractionDigits是用来控制输出字符串的格式的,只会影响最终显示的小数位数,但不会改变底层数值的存储精度。所以这些设置无法解决浮点数本身的近似问题。
解决方案
方案1:让NumberFormatter返回Decimal类型(强烈推荐)
Decimal(对应OC的NSDecimalNumber)是专门为精确十进制计算设计的类型,不会出现二进制浮点数的精度丢失问题。只需要给你的Formatter添加一行配置,让它返回Decimal类型的数值:
extension NumberFormatter { static public func defaultCurrencyFormatter() -> NumberFormatter { let formatter = NumberFormatter() formatter.usesGroupingSeparator = true formatter.numberStyle = .currency formatter.currencySymbol = "" formatter.minimumFractionDigits = 1 formatter.maximumFractionDigits = 2 formatter.currencyGroupingSeparator = "," formatter.currencyDecimalSeparator = "." // 关键配置:让Formatter生成Decimal类型的数值 formatter.generatesDecimalNumbers = true return formatter } } // 测试代码 let formatter = NumberFormatter.defaultCurrencyFormatter() if let decimalNumber = formatter.number(from: "88.99") as? NSDecimalNumber { print(decimalNumber.stringValue) // 输出 "88.99",完美匹配输入值 }
方案2:用Formatter重新格式化数值(临时 workaround)
如果因为某些限制不能使用Decimal,可以在拿到Double值后,不要直接取stringValue,而是用同一个Formatter把数值重新转成格式化后的字符串,这样就能得到正确的两位小数结果:
let formatter = NumberFormatter.defaultCurrencyFormatter() if let number = formatter.number(from: "88.99") { let correctedString = formatter.string(from: number) print(correctedString) // 输出 "88.99" }
针对你的自定义控件的优化建议
- 存储用户输入的金额时,用
Decimal类型代替Double,避免精度丢失; - 从编辑模式切换到显示模式时,用Formatter把
Decimal转成带格式的字符串(比如"$ 88.99")给UILabel; - 再次进入编辑模式时,直接使用存储的
Decimal的stringValue(或者用Formatter生成不带格式的纯数字字符串)填充UITextField,确保编辑的数值和用户之前输入的完全一致。
内容的提问来源于stack exchange,提问作者user824249
相关产品推荐
相关产品推荐

