SwiftUI中CurrencyTextField的Double精度存储异常问题
解决SwiftUI CurrencyTextField的Double精度问题
问题本质
你遇到的是浮点数的天然特性:Double/Float无法精确表示所有十进制小数,比如0.92这类值在二进制浮点数中会被存储为近似值(0.92000000000000004),这不是代码bug,是浮点数的存储机制导致的。你之前用的rounded()默认是取整到整数,自然解决不了两位小数的精度问题。
最优解决方案:用精确数值类型存储货币
货币是需要绝对精确的场景,永远不要用Double/Float存储,推荐用以下两种方案:
方案1:使用Decimal类型(推荐)
Decimal是十进制浮点类型,能精确表示货币需要的小数位,修改你的CurrencyTextField代码如下:
public struct CurrencyTextField<Content>: View where Content: View { @Binding var value: Decimal // 将Double替换为Decimal @State private var money: NSNumber? public init( value: Binding<Decimal>, // 其他参数保留 ) { self._value = value if self.value > 0 { // 用Decimal初始化NSNumber self._money = State(initialValue: NSNumber(decimal: self.value)) } } public var body: some View { FormatSumTextField( numberValue: self.$money, // 其他参数保留 ) .onChange(of: self.money) { moneyValue in // 将NSNumber转换为Decimal self.value = moneyValue?.decimalValue ?? 0 } } }
方案2:用Int存储分(无精度损失)
如果不需要直接处理小数,可以把金额转换为最小单位(比如分),用Int存储:
- 0.92元 → 存储为92(Int类型)
- 显示时再转换回元,用
NumberFormatter处理格式
临时兼容方案(必须用Double时)
如果因为现有架构限制只能用Double,需要实现指定小数位数的精确取整,而不是默认的rounded():
- 给Double添加扩展方法:
extension Double { // 保留指定小数位的取整 func rounded(toPlaces places: Int) -> Double { let multiplier = pow(10, Double(places)) return (self * multiplier).rounded() / multiplier } }
- 在
onChange中使用这个方法:
.onChange(of: self.money) { moneyValue in // 保留两位小数 self.value = moneyValue?.doubleValue.rounded(toPlaces: 2) ?? 0 }
注意:这种方案只是减少了显示异常,但Double本身的精度问题依然存在,复杂计算场景仍可能出现误差,仅推荐作为临时兼容方案。
内容的提问来源于stack exchange,提问作者Specter
相关产品推荐
相关产品推荐

