咋眼一看,好像有几个名词。
浮点数,相对定点数而言。定点数,约定机器中所有数据的小数点位置是固定不变的。由此可见,定点数的形式不灵活,固定的小数点位置决定了固定位数的整数部分和小数部分,不利于表达特别大或特别小的数。浮点数由此在计算机中更多地应用,可以灵活地表达更大范围的实数。
double/float和bigdecimal是三种类型的浮点数,它们之间有区别也可以转换。
float是单精度类型,精度是8位有效数字,占用4个字节的存储空。第7位数字将会产生四舍五入。所以如果定义一个float变量: float a=1.32366635; 则第7位将产生四舍五入(5及5以下的都将舍去) 。
double是双精度类型,精度是17位有效数字,占用8个字节的存储空间。当不声明的时候,默认小数都用double来表示,所以如果要用float的话,则应该在其后加上f。
1)float类型计算误差
案例:float类型的三个数0.1、0.5、0.1相加,期望得到的结果应该是0.7,实际输出的是0.70000005。
原因:浮点数在系统中使用的是二进制的科学技术发,浮点数运算有误差,并不是所有的小数都可以用二进制浮点数来精确表示。二进制浮点数对货币计算是不适合的,因为它不能将0.1或者10的其他任何负幂精确表示为一个长度有限的二进制小数。
2)double类型计算误差
案例:double类型的三个数0.2、0.5、0.1相加,期望得到的结果应该是0.8,实际输出的0.7999999999999999。
原因:一位不是0或者5的小数,用2进制来标识,长度是无限长的。double属于floating binary point types,也就是说都double型的数值在相加减的时候,会将数值转换成二进制的数值如10001.10010110011再做相加减,但是在转换成二进制代码表示时,存储小数部分的位数会有不够的现象,即无限循环小数,这就是造成微差距的主要原因。
3)double小数转BigDecimal后四舍五入有误差
案例:
double g= 10.35;
BigDecimal bigDecimal = new BigDecimal(g).setScale(1, BigDecimal.ROUND_HALF_UP); //期望得到10.4
System.out.println(bigDecimal.doubleValue());
实际得到的结果是10.3。
原因:定义double g = 10.35在计算机中二进制表示成定义g = 10.34444444444444449,当new BigDecimal(g),g还是10.34444444444444449,所以会出现上面的情况。正确的定义方式是使用字符串构造函数:new BigDecimal("10.35").setScale(1, BigDecimal.ROUND_HALF_UP),这样得到的是10.4;也就是说一定要用BigDecimal(String)构造器,而千万不要用BigDecimal(double),通过正确使用BigDecimal,程序就可以打印出我们所期望的结果。
4)解决浮点数精确计算有误差问题
float和double只能用来做科学计算或者是工程计算,在需要精确答案的地方,避免使用float和double;在商业计算中我们要用java.math.BigDecimal。对于货币计算,使用int、long或BigDecimal,使用BigDecimal并且一定要用String来够造。例如:new BigDecimal("10.35").setScale(1, BigDecimal.ROUND_HALF_UP),或者如
Float a = 10.35f;
BigDecimal b= new BigDecimal(String.valueOf(a));
不将就是发现的源动力。