240614-金额运算大坑(用BigDemal解决)

90 阅读2分钟

《阿里巴巴 Java 开发手册》中提到:“为了避免精度丢失,可以使用 BigDecimal 来进行浮点数的运算”。

float/double 与 BigDecimal对比

float&double

浮点数 float 或 double 运算的时候会有精度丢失的风险
因为十进制转换为二进制时,二进制有长度限制,会有截断问题。
关于浮点数,可以看这篇文章 kaito-kidd.com/2018/08/08/…

BigDecimal

BigDecimal 可以实现对浮点数的运算,不会造成精度丢失。
通常情况下,大部分需要浮点数精确运算结果的业务场景(比如涉及到钱的场景)都是通过 BigDecimal 来做的。

BigDecimal的使用

创建

在创建BigDecimal时为了防止精度丢失,推荐使用它的BigDecimal(String val)构造方法或者 BigDecimal.valueOf(double val) 静态方法来创建对象。 禁止使用BigDecimal(double val)来创建BigDecimal对象,否则还是有精读丢失问题。 示例:

// 建议使用
BigDecimal a = new BigDecimal("1.0");
// 禁止使用
BigDecimal a = new BigDecimal(1.0);

加减乘除

示例:

BigDecimal a = new BigDecimal("1.0");
BigDecimal b = new BigDecimal("0.9");
System.out.println(a.add(b));// 1.9
System.out.println(a.subtract(b));// 0.1
System.out.println(a.multiply(b));// 0.90
System.out.println(a.divide(b));// 无法除尽,抛出 ArithmeticException 异常
System.out.println(a.divide(b, 2, RoundingMode.HALF_UP));// 1.11

精读保留

示例:

BigDecimal m = new BigDecimal("1.255433");
BigDecimal n = m.setScale(3,RoundingMode.HALF_DOWN);
System.out.println(n);// 1.255

比较大小

示例:

BigDecimal a = new BigDecimal("1.0");
BigDecimal b = new BigDecimal("0.9");
System.out.println(a.compareTo(b));// 1

等值比较

使用compareTo()方法,而非equals()方法。 因为 equals() 方法不仅仅会比较值的大小(value)还会比较精度(scale),而 compareTo() 方法比较的时候会忽略精度。

示例:

BigDecimal a = new BigDecimal("1");
BigDecimal b = new BigDecimal("1.0");
System.out.println(a.equals(b));//false

注:
《阿里巴巴 Java 开发手册》中提到:浮点数之间的等值判断,基本数据类型不能用 == 来比较,包装数据类型不能用 equals 来判断。

转String

BigDecimal的toString()方法,在某些场景下会生成科学计数法的值;大部分情况不想要科学计数法格式,所以需要使用toPlainString()方法。

示例:

BigDecimal a = new BigDecimal("0.000000123").setScale(9); 
System.out.println(a.toString());      // 1.23E-7                    
System.out.println(a.toPlainString()); // 0.000000123

参考文章

segmentfault.com/a/119000004…
阿里巴巴java开发手册