如何在计算器应用中规避浮点误差,显示正确计算结果
解决计算器浮点误差的显示问题
直接用Double转字符串会暴露二进制浮点存储的固有误差,要实现原生计算器的显示效果,核心是智能判断结果的有效位数,隐藏无意义的近似尾缀,同时保留合理精度。
具体方案
替换你当前的字符串转换逻辑,实现一套格式化逻辑:
- 优先判断结果是否为整数,是则直接显示整数形式
- 若不是整数,检查是否存在因浮点误差产生的长串重复尾缀(比如
999999或000000),存在则截断到有效部分并修正 - 对于真正的无限循环小数(如1/3),保留8位小数(和原生计算器对齐)
修改后的代码
struct Air: View { @State var data: String = "0" @State var firstNumber: Double = 0.3 @State var secondNumber: Double = 3 @State var result: Double = 0 var body: some View { VStack{ Button("Push"){ result = calculate(first: firstNumber, second: secondNumber) data = formatResult(result) // 替换为格式化函数 }.frame(width: 80, height: 40) .background(Color.gray) .foregroundColor(.white) Text("result: Double = \(result)") .font(.title) Text("data: String = \(data)") .font(.title) } } } func calculate(first: Double, second:Double) -> Double { return first * second } // 核心格式化函数 func formatResult(_ number: Double) -> String { // 判断是否为整数 if number.rounded() == number { return String(Int(number)) } // 转成高精度字符串,用于检测浮点误差尾缀 let highPrecisionStr = String(format: "%.15f", number) // 处理999999尾缀(如0.899999999999999 → 0.9) if let nineRange = highPrecisionStr.range(of: "999999") { let truncatedStr = String(highPrecisionStr[..<nineRange.lowerBound]) if let truncatedNum = Double(truncatedStr) { return cleanFormattedString(truncatedNum + 0.000001) } } // 处理000000尾缀(如0.300000000000000 → 0.3) if let zeroRange = highPrecisionStr.range(of: "000000") { let truncatedStr = String(highPrecisionStr[..<zeroRange.lowerBound]) if let truncatedNum = Double(truncatedStr) { return cleanFormattedString(truncatedNum) } } // 正常情况格式化到8位小数 return cleanFormattedString(number) } // 辅助函数:清理末尾无意义的0和小数点 func cleanFormattedString(_ number: Double) -> String { let formatted = String(format: "%.8f", number) // 正则去掉末尾的0和可能的小数点 return formatted.replacingOccurrences(of: "\\.?0+$", with: "", options: .regularExpression) }
效果说明
- 0.1+0.2会显示为
0.3,0.3×3会显示为0.9 - 1/3会显示为
0.33333333 - 整数结果(如2×3)会显示为
6
内容的提问来源于stack exchange,提问作者Yukhnenko
相关产品推荐
相关产品推荐

