Arm浮点特性(三)浮点数运算操作

经过前面两篇文章的介绍,我们已经准备好介绍浮点数的运算操作了。

IEEE-754标准中的规定

IEEE-754标准中没有提供浮点运算的具体描述(伪代码)。标准中对于浮点运算过程的规定集中为下面这一句话:

Unless otherwise specified, each of the computational operations specified by this standard that returns a numeric result shall be performed as if it first produced an intermediate result correct to infinite precision and with unbounded range, and then rounded that intermediate result, if necessary, to fit in the destination’s format.

这句话的意思是,所有的浮点计算都按照无限精度的方式进行,得到一个无限精度的中间结果;然后在根据设定好的舍入规则将中间结果转换到目标格式,得到最终的结果。这句话读起来非常的“架构”。

中间结果是无限精度的,相同的输入应该得到相同的中间结果。只要舍入规则是一致的,那么在不同平台(包括不同的硬件或不同的编程语言)应该返回相同的结果。

IEEE-754中定义的操作

IEEE-754标准中以伪代码函数的形式定义了浮点操作。IEEE-754标准只定义了函数的形式,没有给出具体的计算过程。IEEE-754标准中用两个大章(第5章Operation和第9章Recommended Operation)定义浮点操作。

在本文中,没有列举出IEEE-754中定义的所有操作,主要是列出在软件(指令)层面能够感知的操作,以及需要特别关注的特点。关于IEEE-754标准定义的所有操作以及每个操作的规则,请参阅IEEE-754标准。

必须的浮点操作(Operation)

IEEE-754标准第5章中定义的操作是必须的。IEEE-754将这写操作分为四类:一般性的计算操作、静默计算操作、信号计算操作或非计算操作。

一般性的计算操作

一般性计算操作产生浮点或整数结果,并且可能触发浮点异常。这主要包括算数操作。

  • addition计算两个浮点数的加法。
  • subtraction计算两个浮点数的减法。
  • multiplication计算两个浮点数的乘法。
  • division计算两个浮点数的除法。
  • squareRoot计算一个浮点数的平方根。
  • fusedMultiplyAdd计算融合的乘加操作,即$$x \times y + z$$。乘法和加法都是以无限精度进行的,不存在额外的舍入引入精度损失。因此,融合的乘加操作得到的结果,与先做乘法再做加法得到的结果可能不同。
  • convertFromInt将有符号/无符号的整数格式转换为浮点数。
  • convertFormat将一个浮点数转换到另一种格式的浮点数。
  • convertToInteger将浮点数转化为整数。
  • roundToIntegral将一个浮点数转换为没有小数的浮点数。比如1.2取整后是1.0,而不是整数1。
  • scaleB将一个浮点数按照其基数进行扩展,即$$x \times 2^N$$。其中N是一个整数。
  • logB返回一个浮点数的指数。

静默计算操作

静默计算操作产生浮点结果,但是不触发浮点异常。

  • copy将源操作数复制到相同格式的目的操作数。
  • negate计算浮点数的相反数,即将浮点数的符号位取反。
  • abs计算浮点数的绝对值,将将浮点数的符号位清零。

信号计算操作

信号计算操作不产生浮点结果,但是可能触发浮点异常。信号计算操作主要就是比较操作。

compare<cc>系列操作比较两个浮点数的操作,并且根据<cc>指定的条件返回真或假。浮点数之间的数值关系分为四种:等于(Equal)、大于(Greater)、小于(Less)或无序(Unordered)。当参与比较的两个数之一是NaN的时候,两个浮点数是无序的,或者说是无法比较的。

<cc>可以从Equal、Greater、GreaterEqual、Less、LessEqual和Unordered中选择。需要注意的是,由于浮点数比较存在四种状态,compareGreater的结果和compareLessEqual的结果并不是完全相反关系。当存在一个操作数是NaN时,这两个操作都返回False。

非计算操作

非计算操作不产生浮点结果,也不触发浮点异常。这种操作一般不在指令层面感知。

推荐的浮点操作(Recommended Operation)

IEEE-754标准第9章中还定义了大量的推荐浮点操作。这些浮点操作不是必须实现的。

额外的算数计算操作

  • 指数操作:exp, expm1, exp2, exp2ml, exp10, exp10ml
  • 对数操作:log, log2, log10, logp1, log2p1, log10p1
  • 极坐标操作:hypot
  • 倒数开方函数:rsqrt
  • 幂次:compound, rootn, powrn, pow, powr
  • 三角函数:sin, cos, tan, sinPi, cosPi, tanPi, asin, acos, atan, atan2, asinPi, acosPi, atanPi, atan2Pi, sinh, cosh, tanh, asinh, acosh, atanh

规约操作

  • 加法规约操作:sum, dot, sumSquare, sumAbs
  • 乘积规约操作:scaledProd, scaledProdSum, scaledProdDiff

最大和最小操作

  • 最大和最小操作:minimum,maximum,minimumNumber,maximumNumber
  • 最大和最小绝对值操作:minimumMagnitude, minimumMagnitudeNumber, maximumMagnitude, maximumMagnitudeNumber

最大和最小操作对于NaN的处理有两种方式。

  • 对于没有Number后缀的操作,如果任一操作数为NaN,那么结果为qNaN。
  • 对于具有Number后缀的操作,如果一个操作数是NaN,而另一个操作数是一个数值,那么结果是数字;如果两个操作数都是NaN,那么返回静默NaN。

Arm架构中支持的浮点操作

Arm架构并没有在基础指令集中支持浮点操作,而是在向量扩展(AdvSIMD,SVE和SME)中支持了浮点操作指令。除了对应于IEEE-754标准规定的必须操作的指令,Arm架构根据实际应用的需要选择实现了一部分推荐浮点操作对应的指令,并且通过必须操作和推荐浮点操作的组合,提供了一些复杂浮点操作的指令。在这一节中,我们归纳SVE指令集中的浮点操作指令,阐述Arm架构如何通过浮点指令支持IEEE-754规定的浮点操作。

Arm的浮点指令的助记符都是以FBF开头:F开头的指令是操作HP/SP/DP的浮点指令以及与FP8相关的浮点指令;BF开头的指令是操作BFloat16的浮点指令。助记符中接下类的字符表示指令执行的功能,比如ADD表示加法、MLA表示融合乘加、CM<cc>表示比较等。由于SVE是向量指令集,因此相同的浮点操作可能提供数据通路不同的多条指令。助记符最后的一个或几个字母用来区分相同操作的不同数据通路,比如V表示规约操作、P表示配对操作、以及L表示目的格式宽度大于输入格式等等。

IEEE-754定义的必须操作

这里列出支持IEEE-754定义的必须操作的浮点指令。

一般性的计算操作

下表中归纳了那些可以直接和IEEE-754操作对应的Arm浮点指令。下表中列出的操作,输入和输出格式是相同的。

操作HP/SP/DPBF16说明
additionFADD
FADDP
BFADD
subtractionFSUB
FSUBR
BFSUB
multiplicationFMUL
FMULX
BFMUL当无穷大与零进行乘法是, FMUL 返回NaN,FMULX 返回2.0。
divisionFDIV
FDIVR
squareRootFSQRT
fusedMultiplyAddFMLA
FMLAL
BFMLA
BFMLAL
在后文中展开说明乘累加指令。
convertFromInt 请看下文。
convertFormat 请看下文。
convertToInteger 请看下文。
roundToIntegralFRINT<r> <r>可以取N/A/M/P/Z/I/X,对应于不同的舍入方向。(在前文中介绍舍入方向)
scaleBFSCALEBFSCALE
logBFLOGB

convertFromIntconvertFormatconvertToInteger操作对应的指令,源操作数格式和目的操作数格式不同。下面表格中,行表示源操作数格式,列表示目的操作数格式。

浮点格式转换指令

源\目的FP8BF16HPSPDP
FP8 BF1CVT
BF2CVT
BF1CVTLT
BF2CVTLT
F1CVT
F2CVT
F1CVTL
F2CVTL
BF16BFCVTN
HPFCVTN FCVT{LT}FCVT
SPFCVTNB
FCVTNT
BFCVT
BFCVTNT
FCVT
FCVTNT
FCVT
FCVTLT
DP FCVTFCVT
FCVTNT
FCVTX
FCVTNT

浮点转换到整形的指令

源\目的Int16Int32Int64
HPFCVTZS
FCVTZU
FCVTZS
FCVTZU
FCVTZS
FCVTZU
SP FCVTZS
FCVTZU
FCVTZS
FCVTZU
DP FCVTZS
FCVTZU
FCVTZS
FCVTZU

整形转换到浮点的指令

源\目的HPSPDP
int16SCVTF
UCVTF
int32SCVTF
UCVTF
SCVTF
UCVTF
SCVTF
UCVTF
int64SCVTF
UCVTF
SCVTF
UCVTF
SCVTF
UCVTF

静默计算操作

操作HP/SP/DPBF16说明
copyFCPY/FMOV
negateFNEG
absFABS

在Arm指令集中,没有为BFloat16格式定义取反和绝对值操作。由于FP16和BFloat16的符号位位置相同,因此可以使用FNEGFABS计算BFloat16的相反数和绝对值。

不过,并不是所有情况都正确,当使能FEAT_AFP时,如果操作数是NaN,FNEGFABS指令会触发异常。使用FNEGFABS操作BFloat16格式的浮点数,可能会导致非预期的浮点异常。(在后续文章中详细展开FEAT_AFP对于浮点操作行为的影响)。

信号计算操作

操作HP/SP/DPBF16说明
compare<cc>FCM<cc> <cc>可以取EQ/GE/GT/NE/LE/LT/UO

IEEE-754定义的推荐浮点操作

额外的算数计算操作

  • FEXPA提供指数操作。
  • FRECPE/FRECPS/FRECPX提供倒数操作。
  • FRSQRTE/FRSQRTS提供倒数开方操作。
  • FTMAD/FTSMUL/FTSSEL提供对于三角函数的加速。

规约操作

  • FADDA/FADDV/FADDQV提供规约加法操作。

最大和最小操作

除了与IEEE定义操作对应的指令,Arm还派生出了规约的最大和最小指令。

操作HP/SP/DPBF16规约,HP/SP/DP说明
maximumFMAX
FMAXP
BFMAXFMINV
FMINQV
maximumNumberFMAXNM
FMAXNMP
BFMAXNMFMINNMV
FMINNMQV
minimumFMIN
FMINP
BFMINFMAXV
FMAXQV
minimumNumberFMINNM
FMINNMP
BFMINNMFMAXNMV
FMAXNMQV

Arm架构定义的浮点指令

除了上述指令之外,Arm架构根据实际应用的需要,定义了一系列复合指令以方便软件使用。根据定义复合指令的思路,可以归纳为如下几类:

与绝对值组合的指令

将操作与绝对值ABS组合,针对源操作数的绝对值进行操作或者对结果取绝对值。

  • 绝对值减法FABD计算两个浮点数的减法然后取绝对值。
  • 比较指令FAC<cc>指令比较两个浮点数的绝对值,<cc>可以取GE/GT/LE/LT
  • 绝对值最大最小指令FAMAXFAMIN指令比较两个浮点数的绝对值,返回较大或者较小的绝对值。

乘累加和乘累减指令

除了IEEE-754定义的融合乘累加操作,Arm还定义了乘累减指令FMLS,即将乘积与目的操作累加前对乘积取反($$z-x\times y$$)。乘累加和乘累减指令的源操作数和目的操作数格式不同。指令将源操作数扩展到目的操作数格式后,再进行计算。

源\目的FP8BF16HPSPDP
FP8 FMLALB
FMLALT
FMLSLB
FMLSLT
FMLALBB
FMLALBT
FMLALTB
FMLALTT
FMLSLBB
FMLSLBT
FMLSLTB
FMLSLTT
BF16 BFMLA
BFMLS
BFMLALB
BFMLALT
BFMLSLB
BFMLSLT
HP FMLA
FMLS
FMLALB
FMLALT
FMLSLB
FMLSLT
SP FMLA
FMLS
DP FMLA
FMLS

进一步,通过调整操作数的顺序,Arm提供了一组源目格式相同的乘累加和乘累减操作:FNML[A|S]/FM[A|S]D/FNM[A|S]D

点积指令

Arm架构定义的点积指令与IEEE-754的dot操作有一些不同。IEEE-754定义的dot操作只有计算乘积和的要求。Arm架构定义的点积指令增加了将乘积和累加到目的操作数的要求。

Arm架构定义了两种点积指令FDOT/BFDOTFMMLA/BFMMLA。这两种指令的区别在于源操作数和目的操作数的数据流。

操作BF16->SPHP->SPFP8->HPFP8->SP
DOTBFDOTFDOTFDOTFDOT
矩阵乘法BFMMLAFMMLAFMMLAFMMLA

针对FP8格式的DOT和矩阵乘法还集成了缩放操作。计算得到的半精度乘积和需要先进行缩放,然后再累加到目的操作数。缩放的系数由FPMR.LSCALE确定。

不同格式的点积指令,具体计算过程存在不同:

  • 对于HP到SP的点积指令,先计算DOT操作得到SP的乘积和,再计算累加操作。乘积和额外引入一次舍入。
  • 对于BF16到SP的点积指令,有两种行为可供配置:

    • 如果FPCR.EBF=0,执行的是分步骤的操作,先计算乘积,然后再将乘积累加到目的寄存器。
    • 如果FPCR.EBF=1,执行的是融合的操作,直接计算得到所有乘积和,中间结果舍入到SP后,再累加到目的寄存器。
  • 对于FP8到FP16/SP的点积指令,整个计算过程(DOT、缩放和累加)都是融合再一起的,不引入额外的舍入。

宏指令

  • FCLAMP和BFCLAMP提供了钳位操作,这是由最大和最小操作组合而成的指令。

    • result = minimumNumber(maximumNumber(Zn, Zd), Zm)
  • FCADD提供复数的加法操作,这是一系列加减操作的宏操作。
  • FCMLA提供复数的乘累加操作,包括一系列的乘法和加减法操作。

浮点指令行为的控制

Arm架构为浮点指令提供了多种不同的行为,即特性FEAT_AFP和FEAT_EBF:

  • FEAT_AFP(替代浮点行为扩展)是为了在硬件层面直接获得与其他架构(主要是X86架构)完全相同的结果,从而便于兼容针对其他平台开发的程序。特性由FPCR.AH比特控制,FPCR.AH=1表示使能AFP。
  • FEAT_EBF16(扩展浮点行为扩展)为BF16指令提供了另一种实现方式。特性由FPCR.EBF比特控制,FPCR.EBF=1表示使能EBF。

本篇中,我们没有讨论特殊值进行浮点运算时的规则,只考虑输入和输出都不是特殊值的浮点运算。FPCR.AH对于规范数值的计算行为没有明显的影响。

FPCR.EBF会影响BFDOTBFVDOTBFMMLABFMOP[A|S]指令的行为。这些指令的核心行为都是两路的乘累加操作。在Arm-ARM中这种操作被称为BFDotAdd,即result=addend+op1_a*op2_a+op1_b*op2_b

  • 如果FPCR.EBF=0时,这些指令执行的是分步骤的操作,首先计算每一个乘积,然后再求和。而且每一步计算都采用Round-to-Odd进行取整,从而减小多步骤运算引入的误差。
    result=add(addend, add(mul(op1_a, op2_a), mul(op1_b, op2_b)))
  • 如果FPCR.EBF=1时,这些指令执行的是融合的规约乘加操作,直接计算得到所有乘积和,取整后再与累加数进行加法。而且每一步操作都服从FPCR.RMode的控制。
    result=add(addend, dot(op1_a, op1_b, op2_a, op2_b)
上一篇:Arm浮点特性(二)浮点数舍入
下一篇:Arm浮点特性(四)浮点特殊值处理规则
推荐阅读
关注数
7
文章数
7
编程、模型、手工
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息