简介
在涉及金钱交易交易计算时,为了避免浮点数计算对精度的影响,我们都会选择BigDecimal来处理。但是BigDecimal的有些方法是有坑的,稍不注意也会产生精度计算的问题。
BigDecimal坑一: 禁止使用java.math.BigDecimal#BigDecimal(double)构造函数方法
示例:
/**
* @author 认知科技技术团队
* 微信公众号:认知科技技术团队
*/
public class Demo {
public static void main(String[] args) {
BigDecimal bigDecimal = new BigDecimal(1.019);
System.out.println(bigDecimal);
}
}
输出:
结果肯定不是我们想要的。我们看一下javadoc给的解释:
浮点数在计算机硬件中存储会丢失精度风险。为了避免这种情况,我们需要使用字符串参数的构造函数:
java.math.BigDecimal#BigDecimal(java.lang.String)
示例:
/**
* @author 认知科技技术团队
* 微信公众号:认知科技技术团队
*/
public class Demo {
public static void main(String[] args) {
BigDecimal bigDecimal = new BigDecimal("1.019");
System.out.println(bigDecimal);
}
}
输出:
BigDecimal坑二: 禁止使用java.math.BigDecimal#valueOf(double)静态方法
示例:
/**
* @author 认知科技技术团队
* 微信公众号:认知科技技术团队
*/
public class Demo {
public static void main(String[] args) {
BigDecimal bigDecimal = BigDecimal.valueOf(1.019111456677999999);
System.out.println(bigDecimal);
}
}
输出:
1.019111456677999999 输入,变成了1.019111456678。这也是由于我们输入的是double类型的精度问题导致。这和坑一情况一致。
BigDecimal坑三:禁止使用java.math.BigDecimal#equals比较
示例:
/**
* @author 认知科技技术团队
* 微信公众号:认知科技技术团队
*/
public class Demo {
public static void main(String[] args) {
BigDecimal a = new BigDecimal("1.0");
BigDecimal b = new BigDecimal("1");
System.out.println(a.equals(b));
}
}
输出:
我们看一下javadoc官方解释:
BigDecimal的equals方法会比较比较其“无标度值”和“标度值”,只有两者都相等才相等,而非我们业务逻辑即金钱关系的相等。为了避免这种情况的发生,我们使用compareTo方法即可。
BigDecimal坑四:禁止使用java.math.BigDecimal#round 进行舍入运算
示例:
/**
* @author 认知科技技术团队
* 微信公众号:认知科技技术团队
*/
public class Demo {
public static void main(String[] args) {
BigDecimal x = new BigDecimal("123.56789");
x = x.round(new MathContext(2, RoundingMode.HALF_UP));
System.out.println(x);
System.out.println("x=" + x.toPlainString());
System.out.println("scale=" + x.scale());
}
}
输出结果:
期望结果123.57,而结果真是意料之外。
因为此round方法只是从左到右进行舍入位数。
比如:123111.56789,从左到右舍入2位,小数部分,结果为:120000
/**
* @author 认知科技技术团队
* 微信公众号:认知科技技术团队
*/
public class Demo {
public static void main(String[] args) {
BigDecimal x = new BigDecimal("123111.56789");
x = x.round(new MathContext(2, RoundingMode.HALF_UP));
System.out.println(x);
System.out.println("x=" + x.toPlainString());
System.out.println("scale=" + x.scale());
}
}
进行舍入运算时,我们可以使用方法:
java.math.BigDecimal#setScale(int, java.math.RoundingMode)
示例:
/**
* @author 认知科技技术团队
* 微信公众号:认知科技技术团队
*/
public class Demo {
public static void main(String[] args) {
BigDecimal x = new BigDecimal("1235.6789");
x = x.setScale(2, RoundingMode.HALF_UP);
System.out.println("x=" + x);
}
}
输出:
小结
【🈲】禁止使用java.math.BigDecimal#BigDecimal(double)构造函数方法;
【🈲】禁止使用java.math.BigDecimal#valueOf(double)静态方法;
【🈲】禁止使用java.math.BigDecimal#equals比较;
【🈲】禁止使用java.math.BigDecimal#round 进行舍入运算;
【👍🏻】使用字符串参数的构造函数java.math.BigDecimal#BigDecimal(java.lang.String)构造BigDecimal,防止精度丢失;
【👍🏻】使用compareTo方法比较BigDecimal对象;
【👍🏻】使用java.math.BigDecimal#setScale(int, java.math.RoundingMode)进行舍入运算;