Java BigDecimal HALF_UP舍入逻辑异常问题及解决方案
简短解决方案
感谢大家提供的专业回复,@PetrJaneček @Matt 为我指明了排查方向。我意识到自己需要重新巩固浮点数运算相关知识,最终发现:使用BigDecimal.valueOf方法(基于double的规范字符串表示生成实例)替代直接调用BigDecimal构造方法,即可解决该问题。
修复后的实现代码如下:
private static Double sampleRound(Double value, int places) { BigDecimal bd1 = BigDecimal.valueOf(value); bd1 = bd1.setScale(places, RoundingMode.HALF_UP); return bd1.doubleValue(); }
@PetrJaneček 分享了浮点数运算精度相关的实用讲解视频,可帮助理解这类问题的底层逻辑。
原始问题
我在开发中遇到了一个非常反常的舍入逻辑问题,希望能找到问题根源。
最初的实现函数如下:
private static Double sampleRound(Double value, int places) { BigDecimal bd1 = new BigDecimal(value); bd1 = bd1.setScale(places, RoundingMode.HALF_UP); return bd1.doubleValue(); }
当传入值1942.945、指定保留2位小数时,我预期返回结果为1942.95,但实际返回1942.94;而传入1942.9451时可正常返回1942.95。我尝试将保留位数调整为3位,传入1942.9445时期望返回1942.944,实际却返回1942.945,逻辑表现完全不符合预期。
实际测试结果如下:
sampleRound(1942.945, 2)-> 1942.94
不符合预期,按规则应返回1942.95sampleRound(1942.9450000000001, 2)-> 1942.95sampleRound(1942.94500000000001, 2)-> 1942.94
该值理论上应和第一个测试用例表现一致
补充说明:我也测试了补一位0的入参场景,我理解数据类型本身的精度限制,但核心疑问是:根据Java官方文档说明,
RoundingMode.HALF_UP的规则为“被丢弃的分数部分≥0.5时进位”,但实际表现似乎仅当分数部分>0.5时才进位,没有满足≥0.5的判定规则。
上述表现尚且可以勉强接受,但3位小数的场景完全无法解释:
sampleRound(1942.9445, 3)-> 1942.945
不符合预期,按照2位小数时的表现逻辑,该用例本应返回1942.944。按照此前0.005分数部分未被判定为≥0.5的逻辑,0.0005的分数部分应该遵循同样的判定规则才对,为何会出现相反的结果?我对此十分困惑,希望得到解答。
此致
内容的提问来源于stack exchange,提问作者Joe
相关产品推荐
相关产品推荐

