Swift中字符串格式浮点数相乘仅首个结果正确的问题排查
问题根因
- 核心逻辑错误:在配置单个列表Cell时,遍历了当前搜索条目下整个
service_details数组,每次循环都直接给当前Cell的价格label赋值,前面所有计算结果都会被后续循环的结果覆盖,最终Cell只会显示最后一次遍历算出的价格。感知到的“只有第一个结果正确”,本质是第一个Cell最终显示的价格刚好和预期的第一个服务价格重合,其余Cell的显示值全是对应条目下最后一个服务的价格,和预期不符。 - 稳定性风险:代码中大量使用强制解包
!,只要接口返回空值、非数字格式的字符串,会直接触发运行时崩溃。 - 精度风险:用
Float做货币金额计算会出现二进制浮点数精度误差,计算结果可能出现8.1999999、12.3400001这类不符合金额展示要求的值。 - 冗余操作:汇率转换因子是接口返回的固定值,不需要每次遍历服务条目都重复做字符串转数值的操作。
修正方案
根据业务场景分两种情况实现,全程使用Decimal做金额计算规避精度问题,同时移除所有强制解包保证稳定性:
场景1:每个Cell对应单条服务明细
如果你的列表每一行对应service_details里的一条服务数据,不要在单Cell配置逻辑里遍历整个服务数组,直接按索引取当前行对应的服务数据计算即可:
// 建议在接口请求完成、拿到返回数据时就提前解析一次转换因子,全局复用 var convFactor: Decimal = 0 if let convStr = searchResult?.result?.conversion_factor?.conv_factor, let convVal = Decimal(string: convStr) { convFactor = convVal } // 以下是cellForRowAt中的Cell配置逻辑 // 先按你的层级拿到当前search_result条目 guard let sectionData = searchResult?.result?.search_result?[indexPath.section], let serviceList = sectionData.service_details, indexPath.row < serviceList.count else { cell.priceLbl.text = "" return } // 直接取当前行对应的服务数据,不需要遍历 let currentService = serviceList[indexPath.row] var displayPrice = "" if let rateStr = currentService.standard_rate_in_usd, let rateVal = Decimal(string: rateStr) { let finalPrice = rateVal * convFactor // 用NumberFormatter格式化金额,固定保留2位小数,符合货币展示规范 let priceFormatter = NumberFormatter() priceFormatter.numberStyle = .decimal priceFormatter.minimumFractionDigits = 2 priceFormatter.maximumFractionDigits = 2 displayPrice = priceFormatter.string(from: finalPrice as NSNumber) ?? "" } cell.priceLbl.text = "£ \(displayPrice) \(currData)"
场景2:单个Cell展示当前搜索条目下所有服务的总价
如果业务要求每个Cell展示对应搜索条目下所有服务的总价,遍历时只做金额累加,等所有服务遍历完成、算出总价后再给label赋值,不要每次循环都修改label内容:
// 同样提前解析好转换因子convFactor guard let sectionData = searchResult?.result?.search_result?[indexPath.row], let serviceList = sectionData.service_details else { cell.priceLbl.text = "" return } var totalPrice: Decimal = 0 for service in serviceList { if let rateStr = service.standard_rate_in_usd, let rateVal = Decimal(string: rateStr) { totalPrice += rateVal * convFactor } } // 遍历完成算完总价再格式化赋值 let priceFormatter = NumberFormatter() priceFormatter.numberStyle = .decimal priceFormatter.minimumFractionDigits = 2 priceFormatter.maximumFractionDigits = 2 let displayPrice = priceFormatter.string(from: totalPrice as NSNumber) ?? "" cell.priceLbl.text = "£ \(displayPrice) \(currData)"
内容的提问来源于stack exchange,提问作者SwiftABC
相关产品推荐
相关产品推荐

