java中浮点型数据的⼤⼩⽐较问题
⼀. 精度
举例:double result = 1.0 - 0.9;
这个结果不⽤说了吧,都知道了,0.09999999999999998
float和double类型主要是为了科学计算和⼯程计算⽽设计的。他们执⾏⼆进制浮点运算,这是为了在⼴泛的数字范围上提供较为精确的快速近似计算⽽精⼼设计的。然⽽,它们并没有提供完全精确的结果,所以我们不应该⽤于精确计算的场合。float和double类型尤其不适合⽤于货币运算,因为要让⼀个float或double精确的表⽰0.1或者10的任何其他负数次⽅值是不可能的(其实道理很简单,⼗进制系统中能不能准确表⽰出1/3呢?同样⼆进制系统也⽆法准确表⽰1/10)。
浮点运算很少是精确的,只要是超过精度能表⽰的范围就会产⽣误差。往往产⽣误差不是因为数的⼤⼩,⽽是因为数的精度。因此,产⽣的结果接近但不等于想要的结果。尤其在使⽤ float 和 double 作精确运算的时候要特别⼩⼼。
Java代码
double result = 1.0 - 0.9;//坏代码
⼆. 解决精度丢失有⼏种⽐较常⽤的⽅法
1. ⽤NumberFormat类来格式化计算结果,按照⾃⼰想要的结果进⾏格式化,缺点就是要⼿动去格式化,舍⼊⽅式不同结果不⼀定精确。Java代码
// 好代码
double result = 1.0 - 0.9;
NumberFormat nf = Instance();// 根据⾃⼰的需求格式化
String resultStr = nf.format(result);
2. 如果不介意⾃⼰记录⼗进制的⼩数点,⽽且数值不⼤,那么可以使⽤long ,int等基本类型,具体⽤int还是long要看涉及的数值范围⼤⼩,缺点是要⾃⼰处理⼗进制⼩数点,最明显的做法就是处理货币使⽤分来计算,⽽不⽤元(只涉及加减)。
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 (Doublepare(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(10, 3);// 三分之⼗
System.out.println(fraction);// 10/3
System.out.println(fraction.floatValue());// 3.3333333
System.out.println(fraction.doubleValue());// 3.3333333333333335
System.out.ProperString());// 三⼜三分之⼀
System.out.duce());// 约分,如2/4约分后1/2
2. 处理数值的NumberUtils类;这个⽐较简单,看看api就可以。封装了⼀些常⽤数字操作⽅法,如数字转换、⽐较,获取⼀个数字数组中最⼤值,最⼩值等。
3. 处理数值范围的Range、NumberRange、IntRange、LongRange、FloatRange、DoubleRange类;
Java代码
// 拿int举例,其他类似
IntRange intRange = new IntRange(100, 200);// 创建⼀个范围
int[] range = Array();// 获取范围内的int
4. 处理随机数的JVMRandom和RandomUtils类。这个也⽐较简单,看看api就可以,获取随机数时⽐较⽅便⽽已。
commons-math⼯具包是apache 上⼀个轻量级⾃容器的数学和统计计算⽅法包,包含⼤多数常⽤的数
值算法,这个进⾏专业数学处理时会⾮常⽅便。
如:计算⽅法,⽅差和⼀组数的概率统计问题;
⼀套拟合曲线的数据点应⽤线性回归问题;
插值问题(可能包括线性、样条等插值);
⽤来进⾏参数拟合的最⼩⼆乘⽅法;
求解⽅程组值问题(例如,求⽅程组的根);
解决系统的线性⽅程组;
求解常微分⽅程;
求最⼩值问题的⽅法;
利⽤Commons Math包产⽣指定要求的随机数;
⽣成随机抽样,数据集;
进⾏统计测试;
繁杂的数学函数,如组⼆项式系数、特殊函数(如gamma, beta functions);
个⼈认为这个包过于专业,如果⽤到了,再查询不迟,知道这个包能解决问题就⾏。
当然,这个包很专业,但是不代表只能解决专业数学问题,处理简单的计算也是很⽅便的。
总结:普通的计算最好使⽤BigDecimal类,这个类可以⾮常精确的计算出结果,⽽且你可以完全控制精度,不⽤额外其他操作,⽽且与基本类型转换都⾮常⽅便。bigdecimal格式化两位小数

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。