You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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():

  1. 给Double添加扩展方法:
extension Double {
    // 保留指定小数位的取整
    func rounded(toPlaces places: Int) -> Double {
        let multiplier = pow(10, Double(places))
        return (self * multiplier).rounded() / multiplier
    }
}
  1. 在onChange中使用这个方法:
.onChange(of: self.money) { moneyValue in
  // 保留两位小数
  self.value = moneyValue?.doubleValue.rounded(toPlaces: 2) ?? 0
}

注意:这种方案只是减少了显示异常,但Double本身的精度问题依然存在,复杂计算场景仍可能出现误差,仅推荐作为临时兼容方案。

内容的提问来源于stack exchange,提问作者Specter

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 16:42:48