Java HALF_EVEN模式下DecimalFormat处理x.715、x.915的舍入误差问题
问题1:现象是规则理解错误还是DecimalFormat存在Bug?
既不是规则理解错误,也不是DecimalFormat的Bug,核心差异是两类API处理double输入的逻辑不同:
1548.215这类十进制小数本身无法被双精度浮点数精确存储,转成double后实际的二进制值要么略大于名义十进制值,要么略小于。BigDecimal.valueOf(double)的实现逻辑是先把double转成对应十进制字符串,再构造精确的BigDecimal对象,相当于拿到了你预期的那个“名义十进制值”,所以舍入时会触发对半分场景下的HALF_EVEN规则。DecimalFormat处理double输入时,是直接读取double的二进制精确值进行计算。以1548.215为例,转成double后的实际值略小于1548.215,距离1548.21更近,自然不会触发对半分的偶数舍入逻辑,最终输出1548.21。
如果直接给DecimalFormat传入对应字符串构造的BigDecimal对象,输出结果就会和BigDecimal.setScale的结果完全一致。
问题2:为什么DecimalFormat不处理浮点数精度问题?
这是DecimalFormat的设计定位决定的:它的职责是对输入的数值本身做格式化,不会额外做“修正浮点数精度误差为用户预期的十进制值”的逻辑。浮点数精度丢失是double类型本身的特性,要做精确的十进制小数运算、格式化,规范方案是直接使用BigDecimal传递数值,而不是依赖格式化类兼容浮点数的先天缺陷。
问题3:String.format的%.2f所用的舍入模式为什么没纳入RoundingMode枚举?
你提到的“遇5及以上向上舍入”就是RoundingMode.HALF_UP,也就是常说的四舍五入,这个模式已经在RoundingMode枚举中存在,不存在未纳入的情况。String.format的%f格式化默认就使用HALF_UP舍入规则,和HALF_EVEN的差异就是对半分场景下的处理逻辑不同。
内容的提问来源于stack exchange,提问作者Tepal
相关产品推荐
相关产品推荐

