前言

本篇文章继续学习字节码指令集中的类型转换指令,看看具体指令是怎么操作数据类型转换?

一、类型转换指令的概述


类型转换指令可以将两种不同的数值类型进行相互转换

这些转换操作一般用于实现用户代码中的显式类型转换操作,或者用来处理字节码指令集中数据类型相关指令无法与数据类型一一对应的问题。

主要有以下两种类型转换:宽化类型转换与窄化类型转换

二、类型转换指令的宽化类型转换


Java虚拟机直接支持以下数值的宽化类型转换(widening numeric conversion,小范围类型向大范围类型的安全转换)。也就是说并不需要指令执行,包括:

  • 从int类型到long、float或者double类型。对应的指令为: i2l、i2f、 i2d。
  • 从long类型到float、double类型。对应的指令为:l2f、l2d
  • 从float类型到double类型。对应的指令为:f2d

简化为: int --> long --> float --> double

接下来我们针对于宽化类型转换的基本测试,请看以下示例代码

public class classcastTest{

    //宽化类型转换
    //针对于宽化类型转换的基本测试
    public void upcast1(){
        int i = 10;
        long l = i;
        float f = i;
        double d = i;
        float fl = l;
        double d1 = l;
        double d2 - fl;
    }
}

接下来我们编译该代码,看看方法的字节码是用什么指令进行类型转换的?

image.png

精度损失问题

================================

宽化类型转换是不会因为超过目标类型最大值而丢失信息的,例如从int转换到 long,或者从int转换到double,都不会丢失任何信息,转换前后的值是精确相等的。

从int、long类型数值转换到float,或者long类型数值转换到double时,将可能发生精度丢失:可能丢失掉几个最低有效位上的值,转换后的浮点数值是根据IEEE754最接近舍入模式所得到的正确整数值。

接下来我们针对于精度损失问题的基本测试,请看以下示例代码

public class classcastTest{

    //举例;精度损失的问题@Test
    public void upcast2(){
        int i = 123123123;
        float f = i;
        system.out.print1n(f);
    }
}
//运行结果如下:
1.2312312E8

此时我们再看看long类型转换为double类型,是否也会有如此的问题?

public class classcastTest{

    //举例;精度损失的问题@Test
    public void upcast2(){
        long l = 123123123123L;
        double d = l;
        ystem.out.print1n(d);
    }
}
//运行结果如下:
1.2312312312312312E17

宽化类型转换实际上是可能发生精度丢失的,但是这种情况不会导致Java虚拟机抛出运行时异常

补充说明

================================

针对于从byte、char和short类型到int类型的宽化类型转换实际上是不存在的。

对于byte类型转为int 虚拟机没有做实质性的转化处理,只是简单地通过操作数栈交换了两个数据

而将byte转为long时,使用的是i21,可以看到在内部byte在这里已经等同于int类型处理,类似的还有short类型,这种处理方式有两个特点:

一方面可以减少实际的数据类型,如果为short和byte都准备一套指令,那么指令的数量就会大增,而虚拟机目前的设计上,只愿意使用一个字节表示指令,因此指令总数不能超过256个,为了节省指令资源,将short和byte当做int处理也在情理之中

另一方面由于局部变量表中的槽位固定为32位,无论是byte或者short存入局部变量表,都会占用32位空间。从这个角度说,也没有必要特意区分这几种数据类型。

三、类型转换指令的窄型转换


转换规则

================================

Java虚拟机也直接支持以下窄化类型转换:

  • 从int类型至byte、short或者char类型。对应的指令有: i2b、i2c、i2s
  • 从long类型到int类型。对应的指令有:l2i
  • 从float类型到int或者long类型。对应的指令有:f2i、f21
  • 从double类型到int、long或者float类型。对应的指令有: d2i、d2l、d2f

接下来我们针对于窄化类型转换的基本测试,请看以下示例代码

public class classcastTest{

    ///窄化类型转换基本的使用
    public void downcast1(){
        int i = 10;
        byte b = (byte)i;
        short s = (short)i;
        char c = (char)i;
        long 1 = 10L;
        int i1 = (int)l;
        byte b1 = (byte)l;
    }
}

接下来我们编译该代码,看看方法的字节码是用什么指令进行类型转换的?

image.png

精度损失问题

================================

窄化类型转换可能会导致转换结果具备木同的正负号、不同的数量级,因此转换过程很可能会导致数值去失精度。

尽管数据类型窄化转换可能会发生上限溢出、下限溢出和精度丢失等情况,但是Java虚拟机规范中明确规雉数值类型的窄化转换指令永远不可能导致虚拟机抛出运行时异常

接下来我们针对于精度损失问题的基本测试,请看以下示例代码

public class classcastTest{

    //窄化类型转换的精度损失@Test
    public void downCast4(){
        int i = 128;
        byte b = (byte)i;
        system.out.printin(b);
    }
}
//运行结果如下:
-128

原因在于32位时的数据,转换数据时将高位砍掉只剩下byte的范围,就变成负数了

image.png

补充说明

================================

当将一个浮点值窄化转换为整数类型T(T限于int或long类型之一)的时候,将遵循以下转换规则:

  • 如果浮点值是NaN,那转换结果就是int或long类型的0
  • 如果浮点值不是无穷大的话,浮点值使用IEEE 754的向零舍入模式取整,获得整数值v

如果v在目标类型T(int或long)的表示范围之内那转换结果就是v。否则将根据v的符号转换为T所能表示的最大或者最小

当将一个double类型窄化转换为 float类型时,将遵循以下转换规则:

通过向最接近数舍入模式舍入一个可以使用float类型表示的数字。最后结果根据下面的规则判断:

  • 如果转换结果的绝对值太小而无法使用float来表示,将返回float类型的正负零。
  • 如果转换结果的绝对值太大而无法使用float来表示,将返回float类型的正负无穷大。
  • 对于double类型的NaN值将按规定转换为float类型的NaN值。

28640
116 声望25 粉丝

心有多大,舞台就有多大