jinggod

  浮点数使用 IEEE(电气和电子工程师协会)格式。 浮点数类型使用 符号位、指数、有效位数(尾数)来表示。要注意一下,尾数的最高

在java中,float 和 double 的结构如下:

类 型 符 号 位 指 数 域 有效位域
float 1位 8位 23位
double 1位 11位 52位

符号位: 0为正,1为负;
指数域: 无符号的,float的偏移量为127(即float的指数范围是-126~127,),double
有效位域 无符号的;

2. 浮点类型的两个需要注意的地方

1)存储的小数的数值可能是模糊值

public static void main(String[] args) {
    double d1 = 0.1;
    double d2 = 0.2;
    
    System.out.println(d1+d2 == 0.3);
    System.out.println(d1+d2);
}

运行结果:

false
0.30000000000000004

  上述的运算结果并不是错误。这是因为无法用二进制来准确地存储的0.3,这是一个无限循环的值,与10进制的1/3很相似。不只是0.3,很多小数都是无法准确地用浮点型表示,其实这是由 小数的十进制转成二进制的算法所决定的,十进制的小数要不断乘2,知道最后的结果为整数才是最后的二进制值,但这有可能怎么也得不到整数,所以最后得到的结果可能是一个 无限值 ,浮点型就无法表示了

  但是对于 整数 来说,在浮点数的有效范围内,则都是精确的。同样,也是由于转换算法:十进制的整数转成二进制的算法是不断对2求余数,所以 不会存在无限值的情况

2)浮点数的有效位及精度

  浮点型所能表示的有效位是有限的,所以哪怕是整数,只要超出有效位数,也只能存储相似值,也就是该数值的最低有效位将会丢失,从而造精度丢失。
  float类型的二进制有效位是24位,对应十进制的7 ~ 8位数字;double类型的二进制53位,对应十进制的10 ~ 11位数字。

double、float类型 所能表示的范围比int、long类型表示的范围要广,也浮点类型属于大类型。但是,并不能完美地表整形,浮点类型的精度丢失会造成一些问题。

public static void main(String[] args) {
    int a = 3000000;
    int b = 30000000;
    float f1 = a;
    float f2 = b;
    System.out.println("3000000==3000001 "+(f1==f1+1));
    System.out.println("30000000==30000001 "+(f2==f2+1));
    
    System.out.println("3000000的有效二进制位数:"+ Integer.toBinaryString(a).length());
    System.out.println("30000000的有效二进制位数:"+ Integer.toBinaryString(b).length());
}

运行结果:

3000000 == 3000001  false
30000000 == 30000001  true
3000000的有效二进制位数: 22
30000000的有效二进制位数: 25

  上面的例子很好体现了精度丢失所带来的后果:30000000==30000001 的比较居然为true了。而造成这种结果的原因就是 30000000的有效二进制位数是25位,超出了float所能表示的有效位24位,最后一位就被舍去,所以就造成在刚加的1也被舍去,因此30000000的加一操作前后的浮点型表示是一样的。

  当然,并不是超出浮点型的有效位就不能精确表示,其实,主要看的是最高有效位与最低非0有效位之间的 “间隙”,如果间隙的在浮点型的有效位数内,自然可以精确表示,因为舍去的低有效位都是0,自然就无所谓了。如果上面的例子的浮点型用的是double就不会丢失精度了,因为double的精度是52位。

3)解决浮点型精度丢失的问题

  浮点型带来精度丢失的问题是很让人头痛的,所以一般情况下,在程序中是不会使用float、double来存储比较大的数据。而商业计算往往要求结果精确。《Effactive Java》书中有一句话:

float和double类型的主要设计目标是为了科学计算和工程计算

JDK为此提供了两个高精度的大数操作类给我们:BigInteger、BigDecimal。

相关文章: