最近在做一个数据平台的时候遇到了好多数据处理的问题,总是出一些莫名其妙的问题,以下逐一说明。
问题描述
问题一:
两个double类型的数据相减得到的结果却在后面加了99999999或者000000004等等的奇怪数字。代码如下:
double n1 = 3112883.04;
double n2 = 2555755.91;
System.out.println("n1=" + n1);
System.out.println("n2=" + n2);
System.out.println("n1-n2=" + (n1-n2));
得到的结果如下:
问题二:
数据自动转换为科学计数的问题。代码如下:
double n = 32548128.62;
System.out.println("n=" + n);
结果如下:
解决办法
BigDecimal介绍
Java在java.math包中提供的API类BigDecimal,用来对超过16位有效位的数进行精确的运算。双精度浮点型变量double可以处理16位有效数。在实际应用中,需要对更大或者更小的数进行运算和处理。float和double只能用来做科学计算或者是工程计算,在商业计算中要用java.math.BigDecimal。BigDecimal所创建的是对象,我们不能使用传统的+、-、*、/等算术运算符直接对其对象进行数学运算,而必须调用其相对应的方法。方法中的参数也必须是BigDecimal的对象。构造器是类的特殊方法,专门用来创建对象,特别是带有参数的对象。
参考文献:https://www.cnblogs.com/linjiqin/p/3413894.html
用法详解
第一步:定义两个double类型的数,采用科学计数法表示。
double num = 2.12E5; //整数部分科学计数
double num1 = 2.12E-5; //小数部分科学计数
System.out.println("num:" + "\n" + num);
System.out.println("num1:" + "\n" + num1);
输出结果:
从结果可以看出,num被自动转换为正常数字;而num1没被转换成正常数字,依旧是科学计数法。这里输出结果就是为了验证后面数字被转换成功。
第二步:直接使用new BigDedcimal(number),将double转换成BigDedcimal对象;在这里number必须是double或者float类型。
System.out.println("将double类型的数据装换为BigDecimal类型后");
//将 double 类型的数据装换为 BigDecimal 类型,通常情况下直接装换没有生么用
BigDecimal bd = new BigDecimal(num);
BigDecimal bd1 = new BigDecimal(num1);
System.out.println("bd:new BigDecimal(num):" + "\n" + bd);
System.out.println("bd1:new BigDecimal(num1)" + "\n" + bd1);
输出结果:
从这里可以看出得到的结果中num就是212000,是我们想要的结果;而num1却不是0.0000212,不是我们想要的结果;这不是说数据没有被转换成功,而是这就是BigDedcimal对象的存数方式,在这里不做过多的讨论。那么如何才能得到我们想要的结果呢?
第三步:使用new BigDedcimal(number).setScale(num, BigDecimal.ROUND_HALF_UP),在这里num是指精确度,你想要精确几位就写几,BigDecimal.ROUND_HALF_UP这是四舍五入的规则,原码中的值等于 4 ,也就是说 <=4 舍掉, >4 进一;这个方法很好的解决了科学计数的问题,同时也能解决问题一。最后还要说两个方法toPlainString()和doubleValue(),toPlainString()是将结果转换问String类型;doubleValue()是将最后的结果转换为double类型,一旦调用了此方法将不能解决科学计数的问题;这两个方法按照不同情形去调用即可。
System.out.println("使用 BigDecimal 将科学计数转换成数字");
/**
* 可以看出将bd1转换为数字是出现了问题,转换后的数据并不是0.0000212;
* 转换后变成了0.00002120000000000000031120939159023919273749925196170806884765625;
* 这种情况是由于double型数据在内存的表示遵循IEEE标准造成的,float也存在这种情况;
* float 和 double 类型在计算是也会出现这种情况,这里不细究原因,只说解决方案;
* 使用 setScale(int newScale, int roundingMode) 函数可以解决这一问题,
* 其中 newScale 指精确的位数; roundingMode 指四舍五入模式;
* BigDecimal.ROUND_HALF_UP 原码中的值等于 4 ,也就是说 <=4 舍掉, >4 进一;
* 通常建议使用后两种方法
*/
System.out.println("第一种方法,不建议使用。bd1.setScale(7, BigDecimal.ROUND_HALF_UP):");
System.out.println(bd1.setScale(7, BigDecimal.ROUND_HALF_UP));
System.out.println("第二种方法。bd1.setScale(7, BigDecimal.ROUND_HALF_UP).toPlainString()");
System.out.println(bd1.setScale(7, BigDecimal.ROUND_HALF_UP).toPlainString());
System.out.println("第s三种方法。bd1.setScale(7, BigDecimal.ROUND_HALF_UP).doubleValue()");
System.out.println(bd1.setScale(7, BigDecimal.ROUND_HALF_UP).doubleValue());
/**
* 补充说明,这种问题还有其他 的办法解决,后面讨论。
* 以上解决方案建议使用后两种,按照不同的情况使用;
* toPlainString()就是转成 String 类型;
* doubleValue()就是转成 double 类型,这个方法不能解决科学计数问题。
*/
输出结果:
以上问题还有其他办法的欢迎留言讨论。后续还将针对以上问题写一下其他解决办法。