算数指令
-
作用:
算术指令用于对两个操作数栈上的值进行某种特定运算,并把结果重新压入操作数栈。 -
分类:
大体上算术指令可以分为两种:对整型数据进行运算的指令与对浮点类型数据进行运算的指令。 -
byte、short、char 和boolean类型说明
在每一大类中,都有针对Java虚拟机具体数据类型的专用算术指令。但没有直接支持byte、short、 char和boolean类型的算术指令,对于这些数据的运算,都使用int类型的指令来处理。此外,在处理boolean、byte、 short和char类型的数组时,也会转换为使用对应的int类型的字节码指令来处理。
- 运算时的溢出
数据运算可能会导致溢出,例如两个很大的正整数相加,结果可能是一个负数。其实Java虚拟机规范并无明确规定过整型数据溢出的具体结果,仅规定了在处理整型数据时,只有除法指令以及求余指令中当出现除数为0时会导致虚拟机抛出异常ArithmeticException。 - 运算模式
- 向最接近数舍入模式:JVM要求在进行浮点数计算时,所有的运算结果都必须舍入到适当的精度,非精确结果必须舍入为可被表示的最接近的精确值,如果有两种可表示的形式与该值一样接近,将优先选择最低有效位为零的;
- 向零舍入模式:将浮点数转换为整数时,采用该模式,该模式将在目标数值类型中选择一个最接近但是不大于原值的数字作为最精确的舍入结果;
- NaN值使用
当一个操作产生溢出时,将会使用有符号的无穷大表示,如果某个操作结果没有明确的数学定义的话,将会使用NaN值来表示。而且所有使用NaN值作为操作数的算术操作,结果都会返回NaN;
所有算术指令
所有的算术指令包括:
加法指令: iadd、 ladd、fadd、dadd
减法指令: isub、 lsub、fsub、dsub
乘法指令:imul、lmul、 fmul、dmul
除法指令: idiv、ldiv、fdiv、ddiv
求余指令: irem、lrem、frem、drem //remainder:余数
取反指令: ineg、 lneg、 fneg、dneg //negation:取反
自增指令: iinc
位运算指令,又可分为:
位移指令: ishl、 ishr、iushr、 lshl、 lshr、lushr
按位或指令: ior、 lor
按位与指令: iand、 land
按位异或指令: ixor、 lxor
比较指令: dcmpg、dcmpl、fcmpg、fcmpl、lcmp
举例
public static int bar(int i) {
return ((i + 1) - 2) * 3/4;
}
字节码指令对应的图示:
一个曾经的案例1
代码:
public void add(){
byte i = 15;
int j=8;
int k=i+j;
}
字节码对应的内存解析:
一个曾经的案例2
代码:
public static void main(String[] args) {
int x = 500;
int y = 100;
int a= x/y;
int b= 50;
System.out.println(a + b);
}
字节码对应的内存解析:
比较指令的说明
- 比较指令的作用是比较栈顶两个元素的大小,并将比较结果入栈。
- 比较指令有:dcmpg、dcmpl、fcmpg、 fcmpl、lcmp。
- 与前面讲解的指令类似,首字符d表示double类型,f表示float , l表示long。
- 对于double和float类型的数字,由于NaN的存在,各有两个版本的比较指令。以float为例,有fcmpg和fcmpl两个指令,它们的区别在于在数字比较时,若遇到NaN值,处理结果不同。
- 指令dcmpl和dcmpg也是类似的,根据其命名可以推测其含义,在此不再赘述。
- 指令lcmp针对long型整数,由于long型整数没有NaN值, 故无需准备两套指令。
举例:
指令fcmpg和fcmpl都从栈中弹出两个操作数,并将它们做比较,设栈顶的元素为v2,栈顶顺位第2位的元素为v1,若v1=v2,则压入0;若v1>v2则压入1;若v1<v2则压入-1。
两个指令的不同之处在于,如果遇到NaN值,fcmpg会压入1,而fcmpl会压入-1。
数值类型的数据,才可以谈大小!