取余运算符是否存在浮点误差?如何判断double值为0.5的整数倍
关于double类型0.5整数倍判断的浮点误差问题解答
问题判断
你担心的浮点误差导致判断失败的情况确实会发生。
根本原因是double为二进制浮点数,仅能精确表示形如 k * 2^n(k、n均为整数)的数值。0.5本身是2^-1,可以被精确存储,如果你传入的是直接书写的0.5整数倍字面量(比如0.5、1.0、2.5等),现有代码判断不会出错。但如果输入值是其他浮点运算得到的、数学上为0.5整数倍的结果,运算过程的精度损失会导致实际存储的值和理论值存在微小偏差,这时候num % 0.5的结果就不会刚好等于0.0,进而触发判断失败。
解决方案
可以根据你的业务精度要求选择以下三种方案:
方案1:误差阈值判断(适合普通业务场景)
新增一个极小的误差允许值(epsilon),只要取余结果足够接近0或者0.5,就判定为符合条件,还可以主动修正数值为最接近的0.5整数倍存储,避免后续误差累积:// 可根据业务精度要求调整epsilon大小,普通场景1e-9足够 private static final double EPSILON = 1e-9; public void setNum(double num) { double remainder = num % 0.5; if (remainder < EPSILON || (0.5 - remainder) < EPSILON) { // 可选:修正为精确的0.5整数倍存储 this.num = Math.round(num * 2) / 2.0; } }方案2:放大转整数判断(逻辑更直观,性能无额外损耗)
0.5的整数倍乘以2后一定是整数,所以可以把输入值放大2倍后,判断是否足够接近整数即可:private static final double EPSILON = 1e-9; public void setNum(double num) { double doubleNum = num * 2; if (Math.abs(doubleNum - Math.round(doubleNum)) < EPSILON) { this.num = Math.round(doubleNum) / 2.0; } }方案3:用BigDecimal做无误差判断(适合金融、高精度计量等零误差要求的场景)
完全规避二进制浮点误差,用十进制运算做精确判断:import java.math.BigDecimal; import java.math.RoundingMode; public void setNum(double num) { BigDecimal bdVal = BigDecimal.valueOf(num); BigDecimal multiplyRes = bdVal.multiply(BigDecimal.valueOf(2)); BigDecimal roundedRes = multiplyRes.setScale(0, RoundingMode.HALF_UP); // 偏差小于1e-9即判定符合要求 if (multiplyRes.subtract(roundedRes).abs().doubleValue() < 1e-9) { this.num = roundedRes.divide(BigDecimal.valueOf(2)).doubleValue(); } }
内容的提问来源于stack exchange,提问作者rhkoulen
相关产品推荐
相关产品推荐

