要判断一个整数是否大于,等于或小于另一个整数,可以通过CMP指令,再加上EFLAGS寄存器里的各种标志来进行判断,但是,要比较两个浮点数的大小就没有这么简单,在FPU里提供了一些专门用于浮点数比较的指令...

    本文由zengl.com站长对
    http://pan.baidu.com/share/link?shareid=3717576860&uk=940392313 汇编教程英文版相应章节进行翻译得来。
    另外再附加一个英特尔英文手册的共享链接地址:
    http://pan.baidu.com/share/link?shareid=2345340326&uk=940392313 (在某些例子中会用到)

    本篇翻译对应汇编教程英文原著的第290页到第303页,翻译完前9章 (注意这里的页数不是页脚的页数,而是pdf电子档顶部,分页输入框中的页数,也就是包含了目录,前言部分的总页数,pdf电子档总页数是577,当前已翻译完的页数为303 / 577)。

Floating-Point Conditional Branches 浮点条件分支指令:

    要判断一个整数是否大于,等于或小于另一个整数,可以通过CMP指令,再加上EFLAGS寄存器里的各种标志来进行判断,但是,要比较两个浮点数的大小就没有这么简单,在FPU里提供了一些专门用于浮点数比较的指令。

The FCOM instruction family FCOM指令集:

    FCOM指令集可以将ST0里的浮点值与其他FPU寄存器或内存里的浮点值进行比较,该指令集中可用的指令如下表所示:

Instruction 指令 Description 描述
FCOM Compare the ST0 register with the ST1 register. 
将ST0与ST1里的浮点数进行比较
FCOM ST(x) Compare the ST0 register with another FPU register.
将ST0与ST(x)进行比较
FCOM source Compare the ST0 register with a 32- or 64-bit 
memory value. 
将ST0与32位或64位内存里的浮点数进行比较
FCOMP Compare the ST0 register with the ST1 register value 
and pop the stack. 
将ST0和ST1进行比较,然后将ST0弹出寄存器栈
FCOMP ST(x) Compare the ST0 register with another 
FPU register value and pop the stack. 
将ST0与ST(x)进行比较,然后将ST0弹出寄存器栈
FCOMP source Compare the ST0 register with a 32 or 64-bit 
memory value and pop the stack. 
将ST0与32位或64位内存里的浮点数进行比较,然后将ST0弹出寄存器栈
FCOMPP Compare the ST0 register with the ST1 register 
and pop the stack twice. 
将ST0与ST1进行比较,然后将ST0与ST1都弹出寄存器栈
FTST Compare the ST0 register with the value 0.0. 
将ST0与浮点值0.0进行比较

    上面这些指令比较的结果会被设置到FPU状态寄存器的C0、C2和C3这三个条件代码位里,可能的比较结果如下表所示:

Condition 比较条件 C3 C2 C0
ST0 > source 0 0 0
ST0 < source 0 0 1
ST0 = source 1 0 0

    下面的fcomtest.s程式就演示了FCOM比较指令的用法:

# fcomtest.s - An example of the FCOM instruction
.section .data
value1:
    .float 10.923
value2:
    .float 4.5532
.section .text
.globl _start
_start:
    nop
    flds value1
    fcoms value2
    fstsw
    sahf
    ja greater
    jb lessthan
    movl $1, %eax
    movl $0, %ebx
    int $0x80
greater:
    movl $1, %eax
    movl $2, %ebx
    int $0x80
lessthan:
    movl $1, %eax
    movl $1, %ebx
    int $0x80
 

    上面代码先通过flds value1指令将value1里的10.923加载到ST0,然后使用fcoms value2指令将ST0里的10.923与value2内存里的4.5532进行比较。

    接着,代码里使用了一个技巧来检测浮点比较的结果:先用fstsw指令将FPU状态寄存器里的值设置到AX寄存器,再通过sahf指令将AH里的值加载到EFLAGS寄存器,之所以这么做,是因为AH寄存器的0 , 2 , 4 , 6 及 7位依次对应EFLAGS寄存器里的carry进位,parity奇偶,aligned对齐,zero是否为零,及sign符号标志位,而当我们通过fstsw指令将状态寄存器的值设置到AX后,AH里的0 , 2 及 6位正好就是C0,C2和C3的值,这样经过fstsw,sahf两条指令后,FPU状态寄存器和EFLAGS寄存器就会有如下的对应关系:
  • FPU状态寄存器里的C0对应EFLAGS寄存器里的carry进位标志
  • 状态寄存器里的C2对应EFLAGS寄存器里的parity奇偶标志
  • 状态寄存器里的C3对应EFLAGS寄存器里的zero是否为零标志
    FPU里的三个条件代码位映射到EFLAGS标志寄存器后,就可以使用JA,JB,JZ之类的跳转指令来根据浮点比较的结果,执行不同的处理了,例如前面提到过,当ST0 = source时,C3就会被设置为1,那么经过映射后,对应的zero标志也就会为1,再根据前面流程控制篇里的介绍,JZ指令就会发生跳转,其他的大于,小于的情况也是同理。

    之所以可以使用这一技巧来检测浮点的比较结果,都要归功于英特尔工程师的巧妙设计。

    fcomtest.s程式会根据浮点数的比较结果产生不同的退出码,在linux命令行下可以使用echo $?命令来进行查看:

$ ./fcomtest
$ echo $?

2
$

    上面输出结果为2,表示代码里value1内存里的值大于value2内存里的值。你也可以修改代码,使用不同的值来进行测试。

    在检测两个浮点数是否相等时,要注意的是:浮点数在加载到FPU寄存器后,会被转为双精度扩展的浮点格式,在这一转化过程里,有可能会产生一些舍入上的误差值,这样,标准单精度或标准双精度浮点数在加载到FPU寄存器后,不一定会完全等于原来的值,所以,在这种情况下,进行等于比较时,最好是通过它们的差值是否在一定的误差范围内来进行判断。

The FCOMI instruction family FCOMI指令集:

    前面的fcomtest.s例子是通过fstsw和sahf两条指令来建立起FPU状态寄存器和EFLAGS标志寄存器之间的映射关系的,那么是否存在一种指令,能够在比较后,自动完成这一映射呢?答案是:可以的。

    从Pentium Pro处理器开始,就可以使用FCOMI指令在比较浮点数后,自动将FPU状态寄存器里C0,C2和C3的值设置到EFLAGS寄存器的carry进位,parity奇偶,及zero是否为零标志位。

    下表显示了FCOMI指令集里可用的指令:

Instruction 
指令
Description 
描述
FCOMI ST(x),ST Compare the ST0 register with the ST(x) register. 
将ST0与ST(x)进行比较
FCOMIP ST(x),ST Compare the ST0 register with the ST(x) register 
and pop the stack.
将ST0与ST(x)进行比较,然后将ST0弹出寄存器栈
FUCOMI ST(x),ST Check for unordered values before the comparison.
在比较之前,先检测需要比较的值是否是有效的浮点数
FUCOMIP ST(x),ST Check for unordered values before the comparison 
and pop the stack afterward. 
在比较前,先检测是否是有效的浮点数,比较后,再将ST0弹出寄存器栈

    上面这些FCOMI指令只能比较两个FPU寄存器里的值,不能直接和内存里的浮点数进行比较,所以如果要对内存里的值进行FCOMI的比较,就必须先将内存里的值加载到FPU寄存器。

    上表中,最后两条FUCOMI和FUCOMIP指令在进行比较前,会先利用FPU的tag标记寄存器来判断需要比较的值是否是有效的浮点数,如果不是有效的浮点数,就会抛出异常。

    FCOMI指令的比较结果可以直接使用EFLAGS寄存器里的各种标志来进行判断,如下表所示:

Condition 比较条件 ZF PF CF
ST0 > ST(x) 0 0 0
ST0 < ST(x) 0 0 1
ST0 = ST(x) 1 0 0

    下面的fcomitest.s程式就演示了FCOMI指令的用法:

# fcomitest.s - An example of the FCOMI instruction
.section .data
value1:
    .float 10.923
value2:
    .float 4.5532
.section .text
.globl _start
_start:
    nop
    flds value2
    flds value1
    fcomi %st(1), %st(0)
    ja greater
    jb lessthan
    movl $1, %eax
    movl $0, %ebx
    int $0x80
greater:
    movl $1, %eax
    movl $2, %ebx
    int $0x80
lessthan:
    movl $1, %eax
    movl $1, %ebx
    int $0x80
 

    上面代码里依次将value2和value1加载到FPU寄存器栈,这样加载后,value1里的10.923就会位于ST0,而value2里的4.5532就会位于ST1,然后使用FCOMI指令进行比较后,EFLAGS寄存器里就保存了比较的结果,接着就可以使用JA和JB指令来进行大于,小于的判断操作。

    fcomitest.s经过汇编链接后,测试结果如下:

$ as -gstabs -o fcomitest.o fcomitest.s
$ ld -o fcomitest fcomitest.o
$ ./fcomitest
$ echo $?

2
$

    可以看到FCOMI指令的比较结果和之前提到的FCOM指令的比较结果一致,你也可以使用其他的值进行测试。

The FCMOV instruction family FCMOV指令集:

    类似于整数的CMOV指令,FCMOV指令集可以让你根据条件来进行浮点数的传值,该指令集里的每个指令都是根据EFLAGS标志寄存器里的各种标志来决定是否需要将ST(x)里的值传值到ST0 。

    FCMOV指令通常会配合FCOMI指令来一起使用,因为FCOMI在完成浮点数的大小比较后,会自动设置EFLAGS里对应的标志,而这些标志就可以让FCMOV决定是否需要进行传值操作。

    下表显示了FCMOV指令集里可用的各种指令:

Instruction
指令
Description
描述
FCMOVB Move if ST(0) is below ST(x). 
如果ST0小于ST(x),即EFLAGS里的carry标志被设置时,就进行传值
FCMOVE Move if ST(0) is equal to ST(x). 
如果ST0等于ST(x),即EFLAGS里的zero标志被设置时,就进行传值
FCMOVBE Move if ST(0) is below or equal to ST(x). 
如果ST0小于等于ST(x),即EFLAGS里的carry或zero标志被设置时,就进行传值
FCMOVU Move if ST(0) is unordered. 
浮点无序传值,即当EFLAGS里的Parity标志被设置时,就进行传值
FCMOVNB Move if ST(0) is not below ST(x). 
如果ST0不小于ST(x),即EFLAGS里的carry标志清零时,就进行传值
FCMOVNE Move it ST(0) is not equal to ST(x). 
如果ST0不等于ST(x),即EFLAGS里的zero标志被清零时,就进行传值
FCMOVNBE Move if ST(0) is not below or equal to ST(x). 
如果ST0不小于等于ST(x),即EFLAGS里的carry和zero标志都为零时,就进行传值
FCMOVNU Move if ST(0) is not unordered. 
浮点有序传值,即当EFLAGS里的Parity标志被清零时,就进行传值

    FCMOV指令集的指令格式如下:

fcmovxx source, destination

    上面格式里,source源操作数是ST(x)寄存器,而destination目标操作数是ST(0)寄存器。

    下面的fcmovtest.s程式演示了FCMOV指令的用法:

# fcmovtest.s - An example of the FCMOVxx instructions
.section .data
value1:
    .float 20.5
value2:
    .float 10.90
.section .text
.globl _start
_start:
    nop
    finit
    flds value1
    flds value2
    fcomi %st(1), %st(0)
    fcmovb %st(1), %st(0)
    movl $1, %eax
    movl $0, %ebx
    int $0x80
 

    上面的代码先将value1和value2依次压入FPU寄存器栈,完成压栈操作后,ST0里为10.90,ST1里则为20.5,接着通过FCOMI指令比较ST0和ST1的大小,并根据比较的结果设置EFLAGS里的各种标志,由于ST0小于ST1,所以EFLAGS里的carry标志就会被设置,最后执行FCMOVB指令时,就会将ST1里的20.5传值给ST0,结果就是ST0和ST1里的值都为20.5,该程序调试输出的情况如下:

$ as -gstabs -o fcmovtest.o fcmovtest.s
$ ld -o fcmovtest fcmovtest.o
$ gdb -q fcmovtest

Reading symbols from /home/zengl/Downloads/asm_example/fcmov/fcmovtest...done.
....................................
15        fcmovb %st(1), %st(0)

(gdb) s
16        movl $1, %eax
(gdb) info all
....................................
eflags         0x203    [ CF IF ]
....................................
st0            20.5    (raw 0x4003a400000000000000)
st1            20.5    
....................................
(gdb)

    提示:FCMOV指令集只能用于Pentium Pro及以后的处理器,之前的处理器则不能使用这些指令。

Saving and Restoring the FPU State 保存和恢复FPU的状态:

    之前的章节提到过,FPU的数据寄存器还可以用作MMX的寄存器,用于存放80位的压缩整数,所以如果你的程序既要使用MMX的指令,又要使用FPU的浮点指令的话,就容易出错,庆幸的是,英特尔提供了一些特殊的指令来保存和恢复FPU的的当前工作状态,这样当你进行MMX的工作流程之前,可以先将FPU的当前状态保存到内存里,等MMX相关运算完毕,再切换回之前的工作状态,这样程序就不容易出错了。

    下面就介绍保存和恢复FPU工作状态相关的指令。

Saving and restoring the FPU environment 保存和恢复FPU的环境:

    我们可以使用FSTENV指令来将FPU的当前环境保存到内存里,该指令会将FPU里下面这些寄存器的内容进行保存:
  • Control register 控制寄存器
  • Status register 状态寄存器
  • Tag register 标记寄存器
  • FPU instruction pointer offset FPU指令指针偏移值
  • FPU data pointer FPU数据指针
  • FPU last opcode executed FPU里最后执行过的操作码
    这些值会被保存在一个28字节的内存块里,可以使用FLDENV指令来将内存里保存的这些数据恢复到FPU环境中,下面的fpuenv.s程式就演示了这些指令的用法:

# fpuenv.s - An example of the FSTENV and FLDENV instructions
.section .data
value1:
    .float 12.34
value2:
    .float 56.789
rup:
    .byte 0x7f, 0x0b
.section .bss
    .lcomm buffer, 28
.section .text
.globl _start
_start:
    nop
    finit
    flds value1
    flds value2
    fldcw rup
    fstenv buffer
    finit
    flds value2
    flds value1
    fldenv buffer
    movl $1, %eax
    movl $0, %ebx
    int $0x80
 

    fpuenv.s程式在初始化FPU后,将value1和value2依次加载到FPU寄存器栈,然后修改了控制寄存器,将控制寄存器里的舍入方式设为向上舍入,接着使用FSTENV指令将FPU当前的环境保存到buffer内存位置,在gdb调试器里,可以通过x命令来查看buffer里保存的数据:

(gdb) info float
Status Word:         0x3000                                            
                       TOP: 6
Control Word:        0x0b7f   IM DM ZM OM UM PM
                       PC: Extended Precision (64-bits)
                       RC: Round up
(gdb) x/28bx &buffer 
0x80490c0 <buffer>:	0x7f	0x0b	0xff	0xff	0x00	0x30	0xff	0xff
0x80490c8 <buffer+8>:	0xff	0x0f	0xff	0xff	0xc5	0x3c	0x01	0xc1
0x80490d0 <buffer+16>:	0x60	0x00	0x80	0x03	0xac	0xdc	0x60	0xc5
0x80490d8 <buffer+24>:	0x7b	0x00	0xff	0xff
(gdb) 		

    可以看到buffer内存里的0x7f 0x0b对应控制寄存器里的值,0x00 0x30对应状态寄存器里的值。在数据保存到内存中后,代码再通过finit指令对FPU重新初始化,并重新加载了两个值到寄存器栈,可以通过info float及info all命令来查看重新初始化FPU后寄存器里的值。

    最后,代码使用fldenv buffer指令将buffer里保存的数据恢复到FPU中,FPU里的控制寄存器将恢复为之前设置的向上舍入方式,但是FPU数据寄存器里的值并不会被恢复,因为FSTENV和FLDENV指令只能备份和恢复除了数据寄存器以外的其他寄存器,如果想将数据寄存器里的值也备份到内存,则可以使用下面要介绍的指令。

Saving and restoring the FPU state 保存和恢复FPU的完整工作状态:

    前面的FSTENV和FLDENV指令不能保存数据寄存器,如果想将数据寄存器也一起进行保存和恢复的话,可以使用FSAVE和FRSTOR指令,其中,FSAVE指令用于将FPU里所有的寄存器包括数据寄存器里的值保存到一个108字节的内存块里,然后重新初始化FPU。而FRSTOR指令则用于将FSAVE指令保存的数据恢复到FPU寄存器。

    下面的fpusave.s程式就演示了FSAVE和FRSTOR指令的用法:

# fpusave.s - An example of the FSAVE and FRSTOR instructions
.section .data
value1:
    .float 12.34
value2:
    .float 56.789
rup:
    .byte 0x7f, 0x0b
.section .bss
    .lcomm buffer, 108
.section .text
.globl _start
_start:
    nop
    finit
    flds value1
    flds value2
    fldcw rup
    fsave buffer
    flds value2
    flds value1
    frstor buffer
    movl $1, %eax
    movl $0, %ebx
    int $0x80
 

    在FLDS指令加载数据到寄存器栈,及FLDCW指令将控制寄存器设为向上舍入后,就使用FSAVE指令将FPU里的所有数据保存到buffer内存里,在FSAVE指令执行之前,FPU寄存器的情况如下:

(gdb) info all
.................................
st0            56.78900146484375    (raw 0x4004e327f00000000000)
st1            12.340000152587890625    (raw 0x4002c570a40000000000)
st2            0    (raw 0x00000000000000000000)
st3            0    (raw 0x00000000000000000000)
st4            0    (raw 0x00000000000000000000)
st5            0    (raw 0x00000000000000000000)
st6            0    (raw 0x00000000000000000000)
st7            0    (raw 0x00000000000000000000)
fctrl          0xb7f    2943
fstat          0x3000    12288
ftag           0xfff    4095
.................................
(gdb) info float
  R7: Valid   0x4002c570a40000000000 +12.34000015258789062      
=>R6: Valid   0x4004e327f00000000000 +56.78900146484375         
  R5: Empty   0x00000000000000000000
  R4: Empty   0x00000000000000000000
  R3: Empty   0x00000000000000000000
  R2: Empty   0x00000000000000000000
  R1: Empty   0x00000000000000000000
  R0: Empty   0x00000000000000000000

Status Word:         0x3000                                            
                       TOP: 6
Control Word:        0x0b7f   IM DM ZM OM UM PM
                       PC: Extended Precision (64-bits)
                       RC: Round up
Tag Word:            0x0fff
(gdb)

    可以看到value1和value2里的两个值被加载到寄存器栈,同时fctrl控制寄存器的值为0xb7f即舍入方式被设为Round up向上舍入。

    当FSAVE指令执行完后,FPU寄存器的情况如下:

(gdb) info all
.................................
st0            0    (raw 0x00000000000000000000)
st1            0    (raw 0x00000000000000000000)
st2            0    (raw 0x00000000000000000000)
st3            0    (raw 0x00000000000000000000)
st4            0    (raw 0x00000000000000000000)
st5            0    (raw 0x00000000000000000000)
st6            56.78900146484375    (raw 0x4004e327f00000000000)
st7            12.340000152587890625    (raw 0x4002c570a40000000000)
fctrl          0x37f    895
fstat          0x0    0
ftag           0xffff    65535
.................................
(gdb) info float
  R7: Empty   0x4002c570a40000000000
  R6: Empty   0x4004e327f00000000000
  R5: Empty   0x00000000000000000000
  R4: Empty   0x00000000000000000000
  R3: Empty   0x00000000000000000000
  R2: Empty   0x00000000000000000000
  R1: Empty   0x00000000000000000000
=>R0: Empty   0x00000000000000000000

Status Word:         0x0000                                            
                       TOP: 0
Control Word:        0x037f   IM DM ZM OM UM PM
                       PC: Extended Precision (64-bits)
                       RC: Round to nearest
Tag Word:            0xffff
.................................
(gdb)

    可以看到,FSAVE指令会将FPU进行重新初始化,控制寄存器的值恢复为默认的0x037f,此时舍入方式为默认的Round to nearest(舍入到最接近的值)。之所以value1和value2的值56.78912.34会跑到栈底ST6及ST7的位置,是因为Status状态寄存器由于被重新初始化为默认值,TOP为0,即此时的栈顶ST0对应的是R0寄存器(在FSAVE执行前,ST0对应的是R6寄存器),ST6对应R6,ST7对应R7,所以之前加载的两个数就跑到栈底去了,另外R6和R7显示Empty,是因为Tag标记寄存器也初始化为默认值,所有的数据寄存器此时都被认为是空的,即使R6和R7里有数据,也会被FPU认为是空的,之后再FLDS加载数据时,R7和R6的数据就会被覆盖掉(FPU重新初始化时不会将数据寄存器里的数据清空,只会将其他寄存器初始化为默认值)。

    另外,FSAVE指令还会将FPU之前的数据都保存到buffer内存位置:

(gdb) x/108bx &buffer 
0x80490c0 <buffer>:	0x7f	0x0b	0xff	0xff	0x00	0x30	0xff	0xff
0x80490c8 <buffer+8>:	0xff	0x0f	0xff	0xff	0xc5	0x3c	0x01	0xc1
0x80490d0 <buffer+16>:	0x60	0x00	0x80	0x03	0xec	0x1c	0x4d	0xc5
0x80490d8 <buffer+24>:	0x7b	0x00	0xff	0xff	0x00	0x00	0x00	0x00
0x80490e0 <buffer+32>:	0x00	0xf0	0x27	0xe3	0x04	0x40	0x00	0x00
0x80490e8 <buffer+40>:	0x00	0x00	0x00	0xa4	0x70	0xc5	0x02	0x40
0x80490f0 <buffer+48>:	0x00	0x00	0x00	0x00	0x00	0x00	0x00	0x00
0x80490f8 <buffer+56>:	0x00	0x00	0x00	0x00	0x00	0x00	0x00	0x00
0x8049100 <buffer+64>:	0x00	0x00	0x00	0x00	0x00	0x00	0x00	0x00
0x8049108 <buffer+72>:	0x00	0x00	0x00	0x00	0x00	0x00	0x00	0x00
0x8049110 <buffer+80>:	0x00	0x00	0x00	0x00	0x00	0x00	0x00	0x00
0x8049118 <buffer+88>:	0x00	0x00	0x00	0x00	0x00	0x00	0x00	0x00
0x8049120 <buffer+96>:	0x00	0x00	0x00	0x00	0x00	0x00	0x00	0x00
0x8049128 <buffer+104>:	0x00	0x00	0x00	0x00
(gdb) 

    此时buffer内存里的值不仅包括FPU控制,状态及标记寄存器里的值,还包括所有数据寄存器里的值。在执行完FRSTOR指令后,FPU寄存器的情况如下:

(gdb) info all
...............................
st0            56.78900146484375    (raw 0x4004e327f00000000000)
st1            12.340000152587890625    (raw 0x4002c570a40000000000)
st2            0    (raw 0x00000000000000000000)
st3            0    (raw 0x00000000000000000000)
st4            0    (raw 0x00000000000000000000)
st5            0    (raw 0x00000000000000000000)
st6            0    (raw 0x00000000000000000000)
st7            0    (raw 0x00000000000000000000)
fctrl          0xb7f    2943
fstat          0x3000    12288
ftag           0xfff    4095
...............................
(gdb)

    从上面的输出可以看到,数据寄存器和控制寄存器里的值都恢复为FSAVE执行之前的值了。

Waiting versus Nonwaiting Instructions 等待与非等待指令:

    在之前的FPU寄存器介绍篇里,曾提到过,状态寄存器里有6个浮点异常标志位,如果浮点指令执行时发生了异常(如除零异常),就会设置对应的异常标志位,大部分的浮点指令,如前面提到的FSAVE指令,在执行前都会先检测之前的浮点指令是否产生了异常,如果产生了异常,就必须等待异常处理完毕,再继续执行当前的指令。这些指令就属于等待指令。

    FPU里还存在一种非等待指令,可以不处理之前指令产生的异常,而直接执行,可用的非等待指令如下表所示:

Instruction
指令
Description
描述
FNCLEX Clear the floating-point exception flags 
清理浮点异常标志
FNSAVE Save the FPU state in memory 
保存FPU状态到内存里
FNSTCW Save the FPU control register. 
保存FPU控制寄存器的值
FNSTENV Save the FPU operating environment in memory. 
保存FPU的操作环境到内存里
FNSTSW Save the FPU status register in memory 
or the AX register. 
将FPU状态寄存器的值保存到内存或AX寄存器

Optimizing Floating-Point Calculations 优化浮点计算:

    浮点计算经常是汇编程序里最耗时的部分,英特尔提供了一些建议,可以帮助优化你的汇编浮点计算:
  • 确保要操作的浮点数没有超过数据寄存器可以容纳的尺寸
  • 没有很高精度要求的计算中,将精度控制位设为单精度
  • 对于简单的三角函数计算,尽量使用查询表的方法
  • 尽可能打破依赖链,例如可以将z = a + b + c + d表达式替换为
    x = a + b; y = c + d; z = x + y.这三个表达式。
  • 尽可能的将方程式里的值保存在FPU寄存器里
  • 当整数和浮点数一起计算时,最好将整数加载到FPU寄存器,再进行计算,
    这样比直接和整数进行浮点运算的速度要快,例如FIDIV指令可以替换为:
    先用FILD加载整数到FPU寄存器,再使用FDIVP指令对FPU寄存器里的值进行运算。
  • 尽可能使用FCOMI指令来代替FCOM指令。
    最后就是原著第9章的总结部分,限于篇幅就不多说了,下一篇开始介绍汇编里和字符串操作相关的指令。

    转载请注明来源:www.zengl.com

    OK,就到这里,休息,休息一下 o(∩_∩)o~~
上下篇

下一篇: 汇编字符串操作 (一)

上一篇: 高级数学运算 (三) 高级浮点运算指令

相关文章

汇编里使用Linux系统调用 (一)

基本数学运算 (三) 除法和移位指令

汇编中使用文件 (二)

优化汇编指令 (二)

汇编里使用Linux系统调用 (三) 系统调用结束篇

全书结束篇 使用IA-32平台提供的高级功能 (四) SSE2、SSE3相关指令