【转载】Java 中浮点数的精度丢失问题以及解决方案
Java 中浮点数的精度丢失问题以及解决方案1、Java 中 double 类型操作精度丢失问题2、BigDecimal 类基本介绍3、浮点数精度丢失的解决方案总结参考链接,若侵删https://zhuanlan.zhihu.com/p/1645680011、Java 中 double 类型操作精度丢失问题在使用 Java 中 double 进行运算时,经常出现精度丢失的问题,总是在一个正确的结果
Java 中浮点数的精度丢失问题以及解决方案
参考链接,若侵删
https://zhuanlan.zhihu.com/p/164568001
1、Java 中 double 类型操作精度丢失问题
在使用 Java 中 double 进行运算时,经常出现精度丢失的问题,总是在一个正确的结果左右偏 0.0000**1。
先来看 Java 中 double 类型数值加、减、乘、除计算式实例:
public class Test{
public static void main(String [] args){
System.out.println(0.06+0.01);
System.out.println(1.0-0.42);
System.out.println(4.015*100);
System.out.println(303.1/1000);
}
}
运行结果:
我们发现,计算出来的值和我们预期结果不一致。原因在于我们的计算机是二进制的。浮点数没有办法使用二进制进行精确表示。
计算机的 CPU 表示浮点数由两个部分组成:指数和尾数,这样的表示方法一般都会失去一定的精确度,有些浮点数运算也会产生一定的误差。
浮点运算很少是精确的,只要是超过精度能表示的范围就会产生误差。往往产生误差不是因为数的大小,而是因为数的精度。因此,产生的结果接近但不等于想要的结果。尤其在使用 float 和 double 作精确运算的时候要特别小心
2、BigDecimal 类基本介绍
那么我们如何才能够获取我们想要的预期结果呢?特别是在处理金额交易计算上。
其实 Java 的浮点数只能用来进行科学计算或工程计算,在大多数的商业计算中,一般采用java.math.BigDecimal 类来进行精确计算。
使用步骤:
用 float 或者 double 变量构建 BigDecimal 对象。通常使用 BigDecimal 的构造方法或者静态方法的 valueOf() 方法把基本类型的变量构建成 BigDecimal 对象。
通过调用 BigDecimal 的加,减,乘,除等相应的方法进行算术运算。
把 BigDecimal 对象转换成 float,double,int 等类型。
BigDecimal 类 的构造函数:
BigDecimal(int var) //创建一个具有参数所指定整数值的对象。
BigDecimal(double var) //创建一个具有参数所指定双精度值的对象。
BigDecimal(long var) //创建一个具有参数所指定长整数值的对象。
BigDecimal(String var) //创建一个具有参数所指定以字符串表示的数值的对象。
BigDecimal 类的加,减,乘,除等相应的方法:
BigDecimal add(BigDecimal augend) //加法运算
BigDecimal subtract(BigDecimal subtrahend) //减法运算
BigDecimal multiply(BigDecimal multiplicand) //乘法运算
BigDecimal divide(BigDecimal divisor) //除法运算
3、浮点数精度丢失的解决方案
既然我们知道方法了,那么我们直接套用上面的方法来解决一下上述的问题。
菜鸟误区:
修改以上示例代码,如下:
import java.math.*;
public class Test{
public static void main(String [] args){
double d1 = 0.06;
double d2 = 0.01;
BigDecimal b1 = new BigDecimal(d1);
BigDecimal b2 = new BigDecimal(d2);
System.out.println(b1.add(b2).doubleValue());
double d3 = 1.0;
double d4 = 0.42;
BigDecimal b3 = new BigDecimal(d3);
BigDecimal b4 = new BigDecimal(d4);
System.out.println(b3.subtract(b4).doubleValue());
double d5 = 4.015;
double d6 = 100;
BigDecimal b5 = new BigDecimal(d5);
BigDecimal b6 = new BigDecimal(d6);
System.out.println(b5.multiply(b6).doubleValue());
double d7 = 303.1;
double d8 = 1000;
BigDecimal b7 = new BigDecimal(d7);
BigDecimal b8 = new BigDecimal(d8);
System.out.println(b7.divide(b8).doubleValue());
}
}
我们发现结果还是不对。
上述实例我们调用的构造方法为 BigDecimal(double var)。
注意: 1、BigDecimal(double var) 构造方法的结果有一定的不可预知性。有人可能认为在 Java 中写入 new BigDecimal(0.1) 所创建的 BigDecimal
正好等于 0.1,但是它实际上等于
0.1000000000000000055511151231257827021181583404541015625。这是因为 0.1 无法准确地表示为 double(或者说对于该情况,不能表示为任何有限长度的二进制小数)。这样,传入到构造方法的值不会正好等于
0.1(虽然表面上等于该值)。 2、BigDecimal(String var) 的 String 参数构造方法是完全可预知的:写入 new BigDecimal(“0.1”) 将创建一个 BigDecimal,它正好等于预期的 0.1。因此,比较而言,通常建议优先使用
String 构造方法。
解决方案:
通过上面的示例我们发现:使用 BigDecimal(double val) 构造函数时仍会存在精度丢失问题,建议使用 BigDecimal(String val)。这就需要先把 double 转换为字符串然后在作为BigDecimal(String val) 构造函数的参数。转换为 BigDecimal 对象之后再进行加减乘除操作,这样精度就不会出现问题了。
import java.math.*;
public class Test{
public static void main(String [] args){
double d1 = 0.06;
double d2 = 0.01;
BigDecimal b1 = new BigDecimal(Double.toString(d1));
BigDecimal b2 = new BigDecimal(Double.toString(d2));
System.out.println(b1.add(b2).doubleValue());
double d3 = 1.0;
double d4 = 0.42;
BigDecimal b3 = new BigDecimal(Double.toString(d3));
BigDecimal b4 = new BigDecimal(Double.toString(d4));
System.out.println(b3.subtract(b4).doubleValue());
double d5 = 4.015;
double d6 = 100;
BigDecimal b5 = new BigDecimal(Double.toString(d5));
BigDecimal b6 = new BigDecimal(Double.toString(d6));
System.out.println(b5.multiply(b6).doubleValue());
double d7 = 303.1;
double d8 = 1000;
BigDecimal b7 = new BigDecimal(Double.toString(d7));
BigDecimal b8 = new BigDecimal(Double.toString(d8));
System.out.println(b7.divide(b8).doubleValue());
}
}
运行结果 ,终于对了
总结
(1)需要精确的表示两位小数时我们需要把他们转换为 BigDecimal 对象,然后再进行运算。
(2)使用 BigDecimal(double val) 构造函数时仍会存在精度丢失问题,建议使用 BigDecimal(String val)。
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)