BigDecimal字符串实例化后计算结果异常,求原因及解决方法
BigDecimal字符串实例化计算结果异常问题解析
问题描述
测试代码如下:
import java.math.BigDecimal; import java.math.RoundingMode; public class Test { public static void main(String[] args) { BigDecimal b1 = new BigDecimal("320000"); BigDecimal b2 = new BigDecimal(0.1); BigDecimal b3 = new BigDecimal("9.6"); BigDecimal b5 = new BigDecimal(320000); BigDecimal b6 = new BigDecimal(9.6); BigDecimal b4 = ( b1.multiply(b2) ).multiply( b3.divide(b1, RoundingMode.HALF_UP) ); System.out.println(b4.setScale(4, RoundingMode.HALF_UP)); BigDecimal b7 = ( b5.multiply(b2) ).multiply( b6.divide(b5, RoundingMode.HALF_UP) ); System.out.println(b7.setScale(4, RoundingMode.HALF_UP)); } }
程序输出:
0.0000 0.9600
使用字符串实例化的b1、b3参与计算得到0.0000,但直接用数值实例化的b5、b6得到预期的0.9600,需解释原因并给出解决方法。
原因分析
核心问题出在**new BigDecimal(0.1)的实例化方式**:
0.1是double类型浮点数,二进制无法精确表示十进制的0.1,它实际存储的是一个接近0.1但带有微小误差的近似值。- 第一个计算逻辑中,
b1和b3是用字符串构造的精确BigDecimal值,但b2基于不精确的double值构造,导致b1.multiply(b2)的结果带有误差。后续与b3.divide(b1)的精确结果相乘时,误差被极端放大,最终输出0.0000。 - 第二个计算逻辑中,
b6是用9.6这个double值构造的,它同样带有近似误差,但该误差恰好与b2的误差形成偶然抵消,让最终结果接近预期的0.9600,这种抵消不具备可靠性,不能作为通用解决方案。
解决方案
所有涉及精确计算的BigDecimal实例,必须使用字符串构造方法创建,彻底规避浮点数精度误差:
import java.math.BigDecimal; import java.math.RoundingMode; public class Test { public static void main(String[] args) { // 全部改用字符串构造BigDecimal,确保精度 BigDecimal b1 = new BigDecimal("320000"); BigDecimal b2 = new BigDecimal("0.1"); BigDecimal b3 = new BigDecimal("9.6"); BigDecimal b4 = ( b1.multiply(b2) ).multiply( b3.divide(b1, RoundingMode.HALF_UP) ); System.out.println(b4.setScale(4, RoundingMode.HALF_UP)); // 输出0.9600 } }
此外,可简化计算逻辑,直接计算b2.multiply(b3)——因为(b1*b2)*(b3/b1)等价于b2*b3,减少计算步骤进一步降低误差风险:
BigDecimal b4 = b2.multiply(b3).setScale(4, RoundingMode.HALF_UP);
内容的提问来源于stack exchange,提问作者Ranit Das
相关产品推荐
相关产品推荐

