关闭

Java深海拾遗系列(1)---java浮点型数操作准确性问题刨析

标签: java
175人阅读 评论(0) 收藏 举报
分类:

计算机中的数据存储有整型和浮点型两种形式。
整型是用二进制形式来表示所有正整数和负整数以及零,由于存储空间的限制,只能表示一定范围的整数。在Java中,整型byte,short,int,long,用来表示不同范围的整数。
浮点型则是用二进制的科学计数法形式来表示实数,由于存储空间的限制,不可能精确存储所有的实数,有些实数只能用四舍五入的近似形式存储。浮点型则有float和double用来表示不同精度和范围的浮点数。

浮点型表达的不精确问题

案例一:

public static boolean compareFloatValue(){
float expectedValue = 1.0f;
float realValue =0.0f;
for (int i=0;i<10;i++) {
    realValue +=0.1f;
}
//expectedValue==realValue is false
return expectedValue==realValue;
}

案例二:

double result = 1.0 - 0.9;
//result 0.09999999999999998
System.out.println("result " +result);

案例三:

float larger =10000000.00000f;
float smaller =0.00001f;
//结果是 1.0E7
System.out.print(larger+smaller);

由于精度问题,double/float比较相等也不能直接使用==,但是比较大小可以用<、 >号

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以上  

解决浮点数运算不准确问题方法

用NumberFormat类来格式化计算结果

double result = 1.0 - 0.9;  
NumberFormat nf = NumberFormat.getInstance();// 根据自己的需求格式化  
String resultStr = nf.format(result); 

使用long ,int等基本类型

int resultInt = 10 - 9;  
double result = (double) resultInt / 100;//最终时候自己控制小数点 

使用BigDecimal来代替double

String result = new BigDecimal("1").subtract(new BigDecimal("0.9")) .toString(); 

使用第三方工具类

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  
  1. 处理数值的NumberUtils类;这个比较简单,看看api就可以。封装了一些常用数字操作方法,如数字转换、比较,获取一个数字数组中最大值,最小值等。
  2. 处理数值范围的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

  3. 处理随机数的JVMRandom和RandomUtils类。这个也比较简单,看看api就可以,获取随机数时比较方便而已。

其他

  • 类型声明
    longNum1即使声明为long类型,但是由于没有在数字末尾加上L字符,所以编译器还是以默认的int类型进行存储,longNum2是正确的long类型的声明方式,longNum3由于长度已经超出int类型能够支持的长度,但是又没有在数字末尾加L字符编译器就报错了。

    long longNum1 = 2;
    long longNum2 = 500000000000000000L;
    long longNum3 = 500000000000000000;//提示编译错误

  • 极限值
    正无穷:Double.POSITIVE_INFINITY;
    负无穷:Double.NEGATIVE_INFINITY;
    NaN:Double.NaN;
    并且判断一个数字是否为NaN也应该采用下面的方式
    if(Double.isNaN(Double.NaN)){
    }
0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:355047次
    • 积分:5591
    • 等级:
    • 排名:第4693名
    • 原创:174篇
    • 转载:222篇
    • 译文:2篇
    • 评论:13条
    博客专栏
    最新评论