一. 精度java
举例:double result = 1.0 - 0.9;算法
这个结果不用说了吧,都知道了,0.09999999999999998apache
float和double类型主要是为了科学计算和工程计算而设计的。他们执行二进制浮点运算,这是为了在普遍的数字范围上提供较为精确的快速近似计算而精心设计的。然而,它们并无提供彻底精确的结果,因此咱们不该该用于精确计算的场合。float和double类型尤为不适合用于货币运算,由于要让一个float或double精确的表示0.1或者10的任何其余负数次方值是不可能的(其实道理很简单,十进制系统中能不能准确表示出1/3呢?一样二进制系统也没法准确表示1/10)。api
浮点运算不多是精确的,只要是超过精度能表示的范围就会产生偏差。每每产生偏差不是由于数的大小,而是由于数的精度。所以,产生的结果接近但不等于想要的结果。尤为在使用 float 和 double 做精确运算的时候要特别当心。数组
Java代码 dom
double result = 1.0 - 0.9;//坏代码
二. 解决精度丢失有几种比较经常使用的方法函数
1. 用NumberFormat类来格式化计算结果,按照本身想要的结果进行格式化,缺点就是要手动去格式化,舍入方式不一样结果不必定精确。工具
Java代码 测试
// 好代码 double result = 1.0 - 0.9; NumberFormat nf = NumberFormat.getInstance();// 根据本身的需求格式化 String resultStr = nf.format(result);
2. 若是不介意本身记录十进制的小数点,并且数值不大,那么可使用long ,int等基本类型,具体用int仍是long要看涉及的数值范围大小,缺点是要本身处理十进制小数点,最明显的作法就是处理货币使用分来计算,而不用元(只涉及加减)。spa
Java代码
// 好代码 int resultInt = 10 - 9; double result = (double) resultInt / 100;//最终时候本身控制小数点
3. 使用BigDecimal来代替double,它能让你彻底控制精度,结果会很是精确,加减乘除写起来也很方便,不过他有两个缺点:1,不是基本类型,与基本类型相比,操做起来不方便;2. 速度没有基本类型快,算是用速度换精度。相比第2个缺点并没关系,可是第一个缺点会让你写起代码很不舒服。
Java代码
// 好代码 String result = new BigDecimal("1").subtract(new BigDecimal("0.9")) .toString();
我的比较喜欢这个,并且BigDecimal还支持经常使用的格式化方法,如
BigDecimal num = new BigDecimal("384400000");
String str = new DecimalFormat("地球和月球的距离:#,##,###,###米").format(num);
结果:地球和月球的距离:384,400,000米
三. 浮点类型的比较
因为精度问题,double/float比较相等也不能直接使用==,可是比较大小能够用<、 >号
Java代码
double d1 = 0.1, d2 = 0.1; if (d1 == d2) {}// 坏代码 if (Double.compare(d1, d2) == 0) {}// 好代码 if (Double.doubleToLongBits(d1) == Double.doubleToLongBits(d2)) {}// 好代码 if (Double.valueOf(d1).equals(d2)) {}// 好代码,1.5以上
四. 合理使用第三方工具类
apache的commons-lang工具包中个math包,提供了经常使用的计算方法,数字处理方法,若是项目中涉及到的计算比较多,能够考虑使用commons-math包,这个包过于专业化,估计我们用到的地方很少。
apache的commons-lang的math包,主要有4大类功能
1. 处理分数的Fraction类,分数表示数字,更为精确。
Java代码
Fraction fraction = Fraction.getFraction(10, 3);// 三分之十 System.out.println(fraction);// 10/3 System.out.println(fraction.floatValue());// 3.3333333 System.out.println(fraction.doubleValue());// 3.3333333333333335 System.out.println(fraction.toProperString());// 三又三分之一 System.out.println(fraction.reduce());// 约分,如2/4约分后1/2
2. 处理数值的NumberUtils类;这个比较简单,看看api就能够。封装了一些经常使用数字操做方法,如数字转换、比较,获取一个数字数组中最大值,最小值等。
3. 处理数值范围的Range、NumberRange、IntRange、LongRange、FloatRange、DoubleRange类;
Java代码
// 拿int举例,其余相似 IntRange intRange = new IntRange(100, 200);// 建立一个范围 intRange.containsDouble(111.1);// 是否包含指定数字 int[] range = intRange.toArray();// 获取范围内的int intRange.getMaximumDouble();// 获取最大值,转换成double intRange.getMinimumDouble();// 获取最小值,转换成double
4. 处理随机数的JVMRandom和RandomUtils类。这个也比较简单,看看api就能够,获取随机数时比较方便而已。
commons-math工具包是apache 上一个轻量级自容器的数学和统计计算方法包,包含大多数经常使用的数值算法,这个进行专业数学处理时会很是方便。
如:计算方法,方差和一组数的几率统计问题;
一套拟合曲线的数据点应用线性回归问题;
插值问题(可能包括线性、样条等插值);
用来进行参数拟合的最小二乘方法;
求解方程组值问题(例如,求方程组的根);
解决系统的线性方程组;
求解常微分方程;
求最小值问题的方法;
利用Commons Math包产生指定要求的随机数;
生成随机抽样,数据集;
进行统计测试;
繁杂的数学函数,如组二项式系数、特殊函数(如gamma, beta functions);
我的认为这个包过于专业,若是用到了,再查询不迟,知道这个包能解决问题就行。
固然,这个包很专业,可是不表明只能解决专业数学问题,处理简单的计算也是很方便的。
总结:普通的计算最好使用BigDecimal类,这个类能够很是精确的计算出结果,并且你能够彻底控制精度,不用额外其余操做,并且与基本类型转换都很是方便。