概述
我们都知道,计算机是使用二进制存储数据的。而平常生活中,大多数情况下我们都是使用的十进制,因此计算机显示给我们看的内容大多数也是十进制的,这就使得很多时候数据需要在二进制与十进制之间进行转换。对于整数来说,两种进制可以做到一一对应。而对于小数来讲就不是这样的啦。
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) //除法运算
注:详情可参考API或是查看源码
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)。
以上就是个人关于浮点数精度丢失以及处理的汇总,希望能够帮到大家,有问题的请留言!
最后
以上就是甜美便当为你收集整理的bigdecimal.multiply 精度丢失._Java 中浮点数的精度丢失问题以及解决方案的全部内容,希望文章能够帮你解决bigdecimal.multiply 精度丢失._Java 中浮点数的精度丢失问题以及解决方案所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复