1、说明

1.1、作用

算术指令用于对两个操作数栈上的值进行某种特定运算,并把结果重新压入操作数栈。

1.2、分类

大体上算术指令可以分为两种:对整型数据进行运算的指令与对浮点类型数据进行运算的指令。

1.3、byte、short、char和boolean类型说明

在每一大类中,都有针对Java虚拟机具体数据类型的专用算术指令。但没有直接支持byte、short、char和boolean类型的算术指令,对于这些数据的运算,都使用int类型的指令来处理。此外,在处理boolean、byte、short和char类型的数组时,也会转换为使用对应的int类型的字节码指令来处理。
image.png

1.4、运算时的溢出

数据运算可能会导致溢出,例如两个很大的正整数相加,结果可能是一个负数。其实Java虚拟机规范并无明确规定过整型数据溢出的具体结果,仅规定了在处理整型数据时,只有除法指令以及求余指令中当出现除数为e时会导致虚拟机抛出异常ArithmeticException。

1.5、运算模式

  • 向最接近数舍入模式:JVM要求在进行浮点数计算时,所有的运算结果都必须舍入到适当的精度,非精确结果必须舍入为可被表示的最接近的精确值,如果有两种可表示的形式与该值一样接近,将优先选择最低有效位为零的;
  • 向零舍入模式:将浮点数转换为整数时,采用该模式,该模式将在目标数值类型中选择一个最接近但是不大于原值的数字作为最精确的舍入结果;

    1.6、NaN值使用

    当一个操作产生溢出时,将会使用有符号的无穷大表示,如果某个操作结果没有明确的数学定义的话,将会使用NaN值来表示。而且所有使用NaN值作为操作数的算术操作,结果都会返回 NaN;

2、所有的算术指令

加法指令 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、fcmp1、 lcmp

2,1、举例

例子1:
image.png
fneg是取反操作
image.png
image.png
iinc 2 by 10 是将角标位置2的值(100)取出,进行自增10的操作。

image.png
image.png
~(j - 1)进行取反操作时,jvm内部是做异或操作(ixor),-1在二进制里都是1,两个一样就是0,两个不一样就是1,iand与运算

例子2:
image.png
字节码指令对应的图示:
image.png
image.png

例子3:
代码
image.png
字节码对应的内存解析
image.png
image.png
image.png
例子4:
image.png
image.png
前加加和后加加在不涉及到其他的运算的时候字节码是一样的。
image.png
10是存在局部变量表中为1的位置,iinc 1 by 1是将局部变量表中角标为1的值加1,所以角标为1的位置值变成了11,istore_2是将操作数栈中的10存到角标2的位置,istore_3是将20存入局部变量表中角标位置3,iinc 3 by 1 将角标位置3 自增长1 变成21 然后将21 压入操作数栈中,再将操作数栈中的21存入角标位置4中。

image.png

  1. public void test{
  2. int k = 10;
  3. k = k + (k++) + (++k);
  4. System.out.println(k);//输出结果为32
  5. }
bipush 10 将10放入操作数栈中
istore_1 将10放入局部变量表中角标为1的位置
iload_1 将10从局部变量表中角标为1位置的10压入操作数栈
iinc 1 by 1 将局部变量表中的10自增长1,变成11
istore_1 将操作数栈的10存入局部变量表中角标为1的位置,其实这里就将10覆盖了11
iload_1 将角标数为1的值(10)压入操作数栈(为了进行打印)

总体来说,i++是将局部变量表中的值压入操作数栈中后再将局部变量表中的值进行自增,++i是局部变量表中值进行自增后再压入操作数栈中。

3、比较指令的说明

  • 比较指令的作用是比较栈顶两个元素的大小,并将比较结果入栈。
  • 比较指令有: dcmpg, dcmpl、 fcmpg、fcmpl、 lcmpu。
    • 与前面讲解的指令类似,首字符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两个指令的不同之处在于,如果遇到NaN值,fcmpg会压入1,而fcmpl会压入-1。

数值类型的数据,才可以谈大小!(byte\short\char\int;long\float\double)
boolean、引用数据类型不能比较大小