浮点数的表示

在计算机系统中,浮点数采用 符号+阶码+尾数 进行表示。

java 小数f java 小数比较_浮点数

在Java中,单精度浮点数float类型占32位,它的二进制表示方式为:

  • 符号位:1位,0表示正数; 1表示负数
  • 指数位:8位,用来表示指数(要加上偏移量)
  • 小数位:23位,用来表示小数

实际上计算机中的浮点数表示方法和科学技术法类似,小数的位数决定了浮点数的精度。当一个十进制的小数转换成二进制时,很有可能无法使用这么多小数位表示它。因此使用浮点数的时候,实际存储的尾数是被截取或者舍入之后的值。因此使用浮点数进行计算的时候就不得不考虑精度问题,即浮点数无法精确计算。

如何比较浮点数

对于浮点数,我们无法使用==或者包装类型的equals()来精确比较,尽管无法做到精确比较,但是我们可以确定一个误差范围,当小于这个误差范围的时候就认为这两个浮点数相等。

也可以使用BigDecimal,BigDecimal 是不可变的,它能够精确地表示十进制数字。需要注意的是,创建 BigDecimal 对象时,要使用参数为 String 的构造方法,不要使用构造参数为 double 的,如果非要使用 double 创建,一定要用 valueOf 静态方法,防止丢失精度。然后调用 compareTo 方法比较即可。

为什么使用double的构造参数就会丢失精度?

代码:

System.out.println(new BigDecimal(0.99D));	// 0.9899999999999999911182158029987476766109466552734375

System.out.println(BigDecimal.valueOf(0.99D));	// 0.99

输出:

表面上看起来第一行数字更长,但是我们传的是0.99,它却是0.98…所以损失了精度

public static BigDecimal valueOf(double val) {
        return new BigDecimal(Double.toString(val));
    }


那么使用BigDecimal.valueOf(double val)就一定精确吗?
不是。

代码:

double val = 0.989999999999999111D;
System.out.println(Double.toString(val));   //0.9899999999999991
System.out.println(BigDecimal.valueOf(val));    //0.9899999999999991

当数值超过了double的精度范围,Double.toString()方法也是无法精确表示的。要使用参数为 String 的构造方法才能保证compareTo()的结果是精确的。

System.out.println(new BigDecimal("0.989999999999999111")); //0.989999999999999111


代码

如果可以确保数值不会超过double的精度范围,一定要比较两个浮点型大小,比如不会出现0.989999999999999111这样超过double所能表示的范围的数值,那么:

public static int doubleCompare(double a, double b) {
        BigDecimal bd1 = BigDecimal.valueOf(a);
        BigDecimal bd2 = BigDecimal.valueOf(b);
        return bd1.compareTo(bd2);
    }