前言
在现实中我们都知道:
0.1+0.2=0.3
但是在程序中会出现这样的结果:
0.1+0.2=0.30000000000000004
原因
对于0.1来说,其本质是 1 / 10,那么若你用二进制表示它们,然后除的话,是这样的:1 / 1010,然而这一个是除不尽的,是无穷循环。
===> 0.0 00110011001100110011001100110011... 其中0011循环
而0.2表示为0.0011001100110011...
而在二进制中 1 + 1 = 10,所以 0.1 + 0.2 = 0.0100110011001100...
转成10进制就近似表示为 0.30000000000000004
结论
这是由于计算机采用二进制存储浮点数,而二进制无法准确地表示十进制小数0.1和0.2,会发生精度误差。在计算0.1+0.2时,计算机实际上是计算这两个数的近似值的和,因此得到的结果也是一个近似值,其末尾的小数位可能会有误差。
解决方法
使用Java中的BigDecimal类。
Java中的基本数据类型(如double和float)是有限的,因此它们对于小数计算可能会产生误差。在需要高精度计算时,可以使用Java中的BigDecimal类。
BigDecimal类可以处理大量的小数位数,避免了在浮点数位数过多时出现的不精确问题。以下情况可能需要使用BigDecimal类:
- 当需要精确计算小数,需要避免由于精度丢失而导致错误结果时;
- 当需要进行货币计算时,例如金融应用程序;
- 当需要进行科学计算时,需要高精度的数据存储。
BigDecimal 常用的构造方法如下。
BigDecimal(String val) | 用一个字符串值创建一个BigDecimal对象。 |
BigDecimal(double val) | 用一个double值创建一个BigDecimal对象。 |
BigDecimal(long val) | 用一个long值创建一个BigDecimal对象。 |
BigDecimal(BigInteger val) | 用一个BigInteger值创建一个BigDecimal对象。 |
参数类型为double的构造方法的结果有一定的不可预知性,因为0.1无法准确地表示为 double(或者说对于该情况,不能表示为任何有限长度的二进制小数)。
而参数类型为String 构造方法是完全可预知的:写入 newBigDecimal(“0.1”) 将创建一个 BigDecimal,它正好等于预期的 0.1。因此,比较而言, 通常建议优先使用String构造方法。
下面列出了 BigDecimal 类用于实现加、减、乘和除运算的方法。
add(BigDecimal val) | 将当前BigDecimal对象与另一个BigDecimal对象相加。 |
subtract(BigDecimal val) | 将当前BigDecimal对象与另一个BigDecimal对象相减。 |
multiply(BigDecimal val) | 将当前BigDecimal对象与另一个BigDecimal对象相乘。 |
divide(BigDecimal val) | 将当前BigDecimal对象与另一个BigDecimal对象相除。 |
除此之外BigDecimal 类的其他方法。
compareTo(BigDecimal val) | 比较当前BigDecimal对象与另一个BigDecimal对象的大小。 |
negate() | 将当前BigDecimal对象取相反数。 |
abs() | 将当前BigDecimal对象取绝对值。 |
round(MathContext mc) | 将当前BigDecimal对象按照指定的精度进行四舍五入。 |
intValue() | 将当前BigDecimal对象转换为int类型。 |
doubleValue() | 将当前BigDecimal对象转换为double类型。 |
toString() | 将当前BigDecimal对象转换为字符串。 |
测试代码:
- public class Main {
- public static void main(String[] args) {
- BigDecimal a = new BigDecimal("0.1");
- BigDecimal b = new BigDecimal("0.2");
- BigDecimal c = a.add(b);
- System.out.println(c);
- }
- }
运行结果:
总结
1、在需要精确的小数计算时再使用BigDecimal,BigDecimal的性能比double和float差,在处理庞大,复杂的运算时尤为明显。故一般精度的计算没必要使用BigDecimal。
2、尽量使用参数类型为String的构造函数。
3、BigDecimal都是不可变的(immutable)的, 在进行每一次四则运算时,都会产生一个新的对象 ,所以在做加减乘除运算时要记得要保存操作后的值。