Swift保留X位小数舍入异常:0.99999舍入为1.00而非0.99
解决Swift中特定范围小数舍入的边界问题
你遇到的其实是标准四舍五入规则和自定义边界需求的冲突——0.99999按常规四舍五入到两位小数确实会得到1.00,但你希望在0~1的数值范围内,保留n位小数时,即使舍入后会触达1.00,也停留在该小数位数的最大值(比如两位的0.99),同时其他场景保持正常四舍五入(比如0.348→0.35)。
问题根源
你尝试的三种方法都是遵循标准四舍五入逻辑:0.99999 * 100 = 99.999,round(99.999)会得到100,最终结果自然是1.00。这完全符合数学上的四舍五入规则,但不符合你的特殊边界要求。
解决方案
我们可以在标准舍入的基础上,增加一个边界判断:如果原数小于1,但标准舍入后的结果等于1.0,就返回该小数位数下的最大值(即1.0 - 10^(-decimals)),否则返回正常舍入结果。
方法1:基于浮点运算的快速实现
适合精度要求不是极端严苛的场景:
func customRound(value: Double, decimals: Int) -> Double { guard decimals >= 0 else { return value } // 原数已是1或更大,直接返回1.0 guard value < 1.0 else { return 1.0 } let divisor = pow(10.0, Double(decimals)) var roundedVal = round(value * divisor) / divisor // 若舍入后触达1.0,替换为当前小数位的最大值 if roundedVal == 1.0 { roundedVal = 1.0 - pow(10.0, -Double(decimals)) } return roundedVal } // 测试案例 print(customRound(value: 0.99999, decimals: 2)) // 输出0.99 print(customRound(value: 0.348, decimals: 2)) // 输出0.35 print(customRound(value: 1.0, decimals: 2)) // 输出1.0
方法2:基于NSDecimalNumber的精确实现(推荐)
浮点运算可能存在精度误差(比如某些极接近1的数可能被误判),用NSDecimalNumber可以彻底避免这个问题:
import Foundation func customRoundDecimal(value: Double, decimals: Int) -> NSDecimalNumber { guard decimals >= 0 else { return NSDecimalNumber(value: value) } let one = NSDecimalNumber(value: 1.0) let input = NSDecimalNumber(value: value) // 原数已是1或更大,直接返回1.0 if input.compare(one) != .orderedAscending { return one } // 配置标准四舍五入规则 let behavior = NSDecimalNumberHandler( roundingMode: .plain, scale: Int16(decimals), raiseOnExactness: false, raiseOnOverflow: false, raiseOnUnderflow: false, raiseOnDivideByZero: true ) let roundedVal = input.rounding(accordingToBehavior: behavior) // 若舍入后等于1.0,替换为对应小数位的最大值 if roundedVal.compare(one) == .orderedSame { let divisor = NSDecimalNumber(mantissa: 1, exponent: Int16(-decimals), isNegative: false) return one.subtracting(divisor) } return roundedVal } // 测试案例 print(customRoundDecimal(value: 0.99999, decimals: 2)) // 输出0.99 print(customRoundDecimal(value: 0.348, decimals: 2)) // 输出0.35 print(customRoundDecimal(value: 1.0, decimals: 2)) // 输出1
逻辑验证
这个方案的核心是:
- 对绝大多数数值保持标准四舍五入,满足你0.348→0.35的需求
- 仅当原数小于1,但标准舍入后会得到1.0时,强制将结果设为该小数位数的最大值,完美匹配你对0.99999的预期
内容的提问来源于stack exchange,提问作者Gabriel
相关产品推荐
相关产品推荐

