终于来到了本系列的最后一篇。浮点操作通过异常标志来表示计算出现的不正常现象。软件可以通过这些异常了解浮点操作提供的结果是否可信。
浮点异常
IEEE-754标准定义了五种异常:无效操作(Invalid operation),除零异常(Division by zero)、上溢出(Overflow)、下溢出(Underflow)和不精确(Inexact)。Arm架构同样遵从IEEE-754的定义,并且增了输入非规范数异常(Input Denormal)。
Input Denormal(IDE)
当将非规范数清零时,可能会触发输入非规范数异常:
- 当
FPCR.AH=0
而且FPCR.FZ=1
时,当清零非规范数输入时,产生IDE异常。 - 当
FPCR.AH=1
而且FPCR.FIZ=0
时,非规范数输入会产生IDE异常,此时运算仍然试图操作非规范数。(与x86架构的定义一致)
从伪代码分析,可以发现这两个条件的主要区别在于,当FPCR.AH=0
时,在运算的一开始进行判断,早于对其他异常的检测。当FPCR.AH=1
时,在真正进行运算时才判断是否触发IDE,晚于DZE、IOE和NaN传播的情况。此外,当FPCR.AH=1
时,从SP到BFloat16的转换指令(BFCVT*
)、转换到整形或定点的指令(FCVT*
)、舍入到整数的指令(FRINT*
)不会触发IDE。
不服从这三个比特控制的情况:
- HP和FP8格式的非规范数输入不会触发IDE。
- 当
FPCR.AH=1
时,BFCVT{N|N2|NT}
指令以及BFML[A|S]L{B|T}
指令不产生任何异常。 BFDOT
、BFVDOT
、BFMMLA
和BFMOP[A|S]
指令不产生任何异常。
当将非规范输入清零后,其他异常需要根据清零后的结果判断。
Inexct(IXE)
如果输出结果与无限精度和范围的计算结果不相同,此时产生IXE。这通常在运算的最后步骤检查。
如果使能非规范数输出清零,是否产生IXE取决于FPCR.AH
。如果FPCR.AH=1
,则产生IXE;反之,不产生IXE。
Underflow(UFE)
如果无限精度和范围的计算结果是非规范数,那么会产生UFE异常。UFE异常于FPCR.AH
直接相关:
如果
FPCR.AH=0
,那么在舍入前检查非规范数。- 如果清零非规范数,那么都会触发UFE(但是不会触发捕获)。
- 如果不清零非规范数,那么需要考虑捕获使能UFE。如果使能UFE,那么触发UFE;反之触发UFE。
- 如果
FPCR.AH=1
,那么在舍入后检查非规范数。
Overflow(OFE)
如果指令的计算结果超过目标格式可以标识的范围,那么会产生OFE异常。产生OFE时,结果取决于舍入方式:
- 如果舍入方式为RN,那么结果是正无穷大或负无穷大。
- 如果舍入方式是RP,那么结果是正无穷大或最小负合规数。
- 如果舍入方式是RM,那么结果是负无穷大或最大正合规数。
- 如果舍入方式是RA,那么结果是正无穷大或负无穷大。
- 如果舍入方式是RO,而且用于BFloat16指令,结果是正无穷大或负无穷大。
关于IXE、UFE和OFE三个异常如何检测,可以查看伪代码Arm A-profile A64 Instruction Set Architecture中的FPRoundBase
函数。
Divide by zero(DZE)
只有当有限非零操作数除以零时,则触发DZE异常,同时返回的无穷大作为结果。FDIV
/FDIVR
指令、FRSQRTE
指令、FRECIPE
指令会产生DZE。
DZE和IOE的区别在于:触发DZE时,计算在数学上是成立;触发IOE时,计算在数学上不成立,无法给出结果。所以,无穷大除以零和零除以零都触发IOE,而非DZE。
Invalid Operation(IOE)
当满足下列条件之一时,触发IOE:
- 至少一个操作数是信号NaN。
- 加法、减法以及融合乘加操作中,无穷大减无穷大,比如
addition(+infinity, -infinity)
。FADD
、FSUB
,BFADD
、BFSUB
、FDOT
等指令会因为此规则产生IOE。 - 乘法以及融合乘加操作中,零乘以无穷大。
FMUL
、FDOT
、FMLA
等指令会产生IOE。 - 零除以零,或者无穷大除以无穷大。
FDIV
、FDIVR
等指令会因此产生IOE。 - 计算小于零的操作数的开方。
FSQRT
和FRSQRT
指令会因此产生IOE。
触发IOE异常时,同时返回静默NaN。以上规则与IEEE-754标准一致。
- 对于转换浮点数到整形或定点的指令,如果输入无法用目标格式表达,那么也会产生IOE。
FCMPE
和FCCMPE
指令的输入之一是NaN。- 当
FPCR.AH=1
时,FMAX{P|V}
/FMIN{P|V}
以及BFMAX
/BFMIN
指令的输入之一是NaN,此时总是返回第二个输入。
不触发异常的指令
下列指令不会产生任何异常:
- 指数指令
FEXPA
。 - 三角函数指令
FTSSEL
。 BFDOT
、BFVDOT
、BFMMLA
、BFMOP[A|S]
指令。- 操作SME的ZA矩阵的浮点指令。
FABS
和FNEG
。- 当
FPCR.AH=1
时,BFCVT{N|N2|NT}
以及BFMLAL[T|B]
指令 - 当
FPCR.AH=1
时,FRECPE
、FRECPS
、FRECPX
、FRSQRTE
和FRSQRTS
指令。
浮点异常处理
IEEE-754标准中提供了两种异常处理方式,分别称为默认异常处理和替代异常处理。默认异常处理,也就是不间断异常处理。浮点异常不会中断指令执行,而是将异常通过一些标志记录下来。软件通过查询这些标志获取异常信息。替代异常处理则会发出异常信号,要求硬件或软件执行异常处理程序。Arm架构也支持这两种异常处理方式。
浮点异常累计比特
FPSR
和FPSCR
寄存器提供了累计浮点异常比特:IDC
、IXC
、UFC
、OFC
、DZC
和IOC
,分别对应于输入非规范数、不精确、下溢出、上溢出、除零和无效操作。如果比特为1,表示从上一次软件清零开始,浮点指令触发了对应的异常。如果比特为0,表示从上一次软件清零开始,浮点指令没有触发对应的异常。
这些比特称为“累计”,是因为它们不会被硬件清零。从被软件清零后,直到下一次软件清零前,硬件只会将这些比特置位,不会清零。这就使得,这些比特中保留了从上一次软件清零之后,所有浮点指令生成的异常。
可以通过这些比特获取一连串浮点操作中出现的异常行为。比如下列的序列:
MRS FPSR, 0
...
MSR x1, FPSR
此时X1
获得的FPSR
包括之前所有浮点操作引入的异常。
浮点异常捕获使能比特
FPCR
和FPSCR
寄存器提供了异常捕获使能比特:IDE
、IXE
、UFE
、OFE
、DZE
和IOE
,分别对应于输入非规范数、不精确、下溢出、上溢出、除零和无效操作。
异常捕获使能比特 | 含义 |
---|---|
0b0 | 不捕获对应的异常,设置对应的浮点异常累计比特。 |
0b1 | 捕获对应的异常,不设置对应的浮点异常累计比特。 |
也就是说,如果禁用浮点捕获,那么处理器对于浮点异常的处理服从IEEE-754标准中定义的默认异常处理方式;如果使能浮点捕获,那么处理器对于浮点异常的处理服从IEEE-754标准中定义的替代异常处理方式。
浮点异常一般是被捕获到不低于当前异常等级而且可以处理浮点异常的最低等级。即从EL0捕获到EL1(HCR_EL2.TGE=0
)或EL2(HCR_EL2.TGE=1
),从EL1捕获到EL1,从EL2捕获到EL2,从EL3捕获到EL3。
在ELR_ELx
中用EC=0x1C
报告AArch64状态产生的浮点异常;用EC=0x18
报告AArch32状态产生的浮点异常。当报告浮点异常时,ELR_ELx
的ISS编码如下:
通常,在AArch64状态发生浮点异常时,将设置TFV=1
,并且设置IDF
、IXF
、UFF
、OFF
、DZF
和IOF
比特中的一个或多个为1,从而表示指令触发了哪些浮点异常。
浮点异常是一种精确异常,异常处理程序执行完成后,默认会返回到触发异常的指令的下一条指令。
当使能流式SVE模式时,是否能够触发异常有三种情况:
- 如果指令操作的是ZA寄存器,那么指令已经不能触发异常,因为SME流水线在处理器流水线外,处理器流水线无法追踪和维护SME指令的完成。
- 如果使能了流模式,但是没有使能了FEAT_SME_FA64,那么不以ZA矩阵为目标的SVE和SME浮点指令也不会触发异常。此时,认为异常捕获使能比特都是0。
- 如果使能了流模式而且使能了FEAT_SME_FA64,那么不以ZA矩阵为目标的SVE和SME浮点指令可以触发异常。
异常优先级
Arm架构没有像其他架构那样完成地给出异常优先级。
根据Arm架构推断,可以得出Arm架构的浮点异常优先级:IDE> UFE = OFE > IXE。IOC和DZE不会与这四种异常同时出现,也就没有定义异常优先级的必要。特别提示:异常优先级控制的是一组元素对应的操作引发多个异常时应该报告哪一个异常,与异常检测顺序不同。
对于向量化指令(AdvSIMD和SVE),架构没有定义不同元素触发的浮点异常的优先级。
写在最后
本系列五篇文章梳理了IEEE-754标准和Armv9架构对于浮点的支持。浮点数的规则非常的庞杂而且琐碎,充分体现了“细节是魔鬼”这一论断。保证浮点数计算结果正确不是一句显然的话。
另一方面,对于细节控和技术控来说,浮点数也是非常值得挖掘的。随着研究深入,越发佩服数学家和架构师在数值计算方面做出的努力。
上一篇:Arm浮点特性(四)浮点特殊值处理规则