类型升级
- 规则是低的往高转
- 如果运算符(通常是比较运算符)两边的数值类型不同,则会先进行类型转换:
- 出现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;
}