整数 (CPU) 和 FPU 是相互独立的单元,因此,在执行整数和系统指令的同时可以执行浮点指令。这个功能被称为并行性 (concurrency),当发生未屏蔽的浮点异常时,它可能是个潜在的问题。反之,已屏蔽异常则不成问题,因为,FPU 总是可以完成当前操作并保存结果。
发生未屏蔽异常时,中断当前的浮点指令,FPU 发异常事件信号。当下一条浮点指令或 FWAIT(WAIT) 指令将要执行时,FPU 检查待处理的异常。如果发现有这样的异常,FPU 就调用浮点异常处理程序(子程序)。
如果引发异常的浮点指令后面跟的是整数或系统指令,情况又会是怎样的呢?很遗憾,指令不会检查待处理异常,它们会立即执行。假设第一条指令将其输出送入一个内存操作数,而第二条指令又要修改同一个内存操作数,那么异常处理程序就不能正确执行。示例如下:
设置 WAIT 和 FWAIT 指令是为了在执行下一条指令之前,强制处理器检查待处理且未屏蔽的浮点异常。这两条指令中的任一条都可以解决这种潜在的同步问题,直到异常处理程序结束,才执行 INC 指令。
下面将用几个简短的例子来演示浮点算术运算指令。一个很好的学习方法是用 C++ 编写表达式,编译后,再检查由编译器生成的代码。
现在编写代码,计算表达式 valD=-valA+(valB*valC)。下面给出一种可能的循序渐进的方法:将 valA 加载到堆栈,并取其负数;将 valB 加载到 ST(0),则 valA 成为 ST(1);将 ST(0) 和 valC 相乘,乘积保存在 ST(0) 中;将 ST(1) 与 ST(0) 相加,和数保存到 valD:
.data
valA REAL8 1.5
valB REAL8 2.5
valC REAL8 3.0
valD REAL8 ?; +6.0
.code
fld valA ; ST(0) = valA
fchs ;修改 ST(0) 的符号
fld valB ; 将 valB 加载到 ST(0)
fmul valC ; ST(0) *= valC
fadd ; ST(0) += ST(1)
fstp valD ; 将 ST(0) 保存到 valD
下面的代码计算并显示一个双精度实数数组之和:
ARRAY_SIZE = 20
.data
sngArray REAL8 ARRAY_SIZE DUP(?)
.code
mov esi, 0 ;数组索引
fldz ; 0.0 入栈
mov ecx,ARRAY_SIZE
L1: fld sngArray[esi] ;将内存操作数加载到ST(0)
fadd ; ST(0) 加 ST(1),出栈
add esi,TYPE REAL8 ;移至!I 下一个元素
loop L1
call WriteFloat ;显示 ST(0) 中的和数
FSQRT 指令对 ST(0) 中的数值求平方根,并将结果送回 ST(0)。下面的代码计算了两个数的平方根之和:
.data
valA REAL8 25.0
valB REAL8 36.0
.code
fid valA ; valA 入栈
fsqrt ; ST(0) = sqrt(valA)
fid valB ; valB 入栈
fsqrt ; ST(0) = sqrt(valB)
fadd ; ST (0)+ST(1)
下面的代码计算了表达式 (airay[0]*airay[l]) + (array[2]*array[3])。该计算有时也被称为点积 (dot product)。
下表列出了每条指令执行后,FPU 堆栈的情况。输入数据如下:
指令 | FPU堆栈 | 指令 | FPU堆栈 |
---|---|---|---|
fld array | ST(0):+6.0000000E+000 | fmul [array+12] | ST(0):+1.4400000E+001 |
fmul [array+4] | ST(0):+1.2000000E+001 | ST(1):+1.2000000E+001 | |
fld [array+8] | ST(0):+4.5000000E+000 | fadd | ST(0):+2.6400000E+001 |
ST(1):+1.2000000E+001 |