类型升级

  • 规则是低的往高转
  • 如果运算符(通常是比较运算符)两边的数值类型不同,则会先进行类型转换:
    • 出现double则全转换为double
    • 出现float则全转换为float
    • 出现long则全转换为long
    • 否则全转换为int
  • 混合运算时,必须先把所有的数据都转换成同一数据类型:
  • 字符必须先转换成int型,short转换成int型,float转换成double。

    浮点数的二进制表示

  • float和double存在计算机中分别是这样的:
  • 第一部分为符号位,第二部分为指数位,第三部分为基数位。这是科学计数法的二进制表示。
  • 由于位数是固定的,所以对无限小数不能精确表示,但实际上,对于一些有限小数也不能精确表示,比如0.1。
  • 科学计数法的十进制表示:2.5410^2;二进制表示:1.12^-4(跟前面表示的不是同一个数)。
  • 我们来查看一下浮点数在内存中的样子:

    System.out.println(Integer.toBinaryString(Float.floatToIntBits(0.1f)));
    System.out.println(Long.toBinaryString(Double.doubleToLongBits(0.1d)));
    System.out.println(Long.toBinaryString(Double.doubleToLongBits(0.1f)));
    
  • xxxToXxxBits()是把某类型转换为长度相同的另一类型,底层可能是把浮点数扩大一定倍数吧。
  • toBinaryString()则是获取某个数值的字节码
  • 第三行首先要把float转为double然后再被doubleToLongBits()作用

怎么比较呢

  • 直接用==肯定不行,比如:

    Double a = Double.valueOf("0.0");
    Double b = Double.valueOf("-0.0");
    System.out.println(a.equals(b)); //false
    
  • 用equals也不行:

    Double a = Math.sqrt(-1.0); //NaN
    Double b = 0.0d / 0.0d; //NaN
    Double c = a + 200.0d; //NaN
    Double d = b + 1.0d; //NaN
    System.out.println(a.equals(b)); //true,equals只是比较两个对象的引用是否相同
    System.out.println(b.equals(c)); //true
    System.out.println(c.equals(d)); //true
    
  • 最好也不要用compare To()
    • Float.compareTo()和Double.compareto()虽说是先将数值转换成整型再进行比较的,但实际由于舍入误差,还是可能会导致经过一些计算后得到的待比较浮点数有略微不同;而且无法避免NaN和0.0、-0.0的错误。
  • 自定义阙值进行比较

    • 要考虑NaN、无穷和舍入误差,可能出现的场景如下:

    • 算法

      public boolean isEqual(double a, double b) {

      if (Double.isNaN(a) || Double.isNaN(b) || Double.isInfinite(a) || Double.isInfinite(b)) {
          return false;
      }
      return Math.abs(a - b) < 1e-6d;
      

      }