舍入(Rounding)将无限精度的浮点数转换到某个特定的格式中,需要按照一定的规则在特定格式能够表示的浮点数中寻找与无限精度浮点数的近似。舍入时浮点数处理非常重要的属性,对于浮点数操作的结果影响明显。
Arm架构支持IEEE-754定义的所有五种舍入模式,只是使用了与IEEE标准不同的术语。下表种提供了Arm舍入模式和IEEE-754术语的对应关系。此外,Arm架构还提供了舍入到奇数(Round-to-Odd)模式。
Arm舍入模式 | 缩写 | 名称 | IEEE 754-2008标准术语 |
---|---|---|---|
Round towards Minus Infinity | RM | 向负无穷舍入 | roundTowardNegative |
Round towards Plus Infinity | RP | 向正无穷舍入 | roundTowardPositive |
Round towards Plus Zero | PZ | 向零舍入 | roundTowardZero |
Round to Nearest | RN | 向最近舍入 | roundTiesToEven |
Round to Nearest with Ties to Away | 向外舍入 | roundTiesToAway | |
Round to Odd | 向奇数舍入 |
为了方便,在本文中将Round to Nearest with Ties to Away缩写为RA,将Round to Odd缩写为RO。(RA和RO不是Arm官方认可的缩写。)
舍入模式
六种舍入模式的规则如下:
- RM选择距离最近的两个浮点数中数值更小的那一个。
- RP选择距离最近的两个浮点数中数值更大的那一个。
- RZ选择距离最近的两个浮点数中绝对值更小的哪一个。
- RN选择距离最近的两个浮点数中,距离更近的一个。如果两个浮点数距离相同,选择偶数的那一个。这种舍入方法俗称“银行家舍入”。
- RA选择距离最近的两个浮点数中,距离更近的一个。如果两个浮点数距离相同,选择绝对值更大的那一个。这种舍入方法俗称为“四舍五入”。
- RO将源操作数舍入到目标格式。当舍入过程存在不精确的部分时,保证最低位一定是1。可以认为RO分为两步,第一步是进行RZ,第二步则是在存在误差时保证最低位是1。
假设利用这六种舍入模式将一个小数舍入到整数,那么输入和输出关系如下图所示。图中实心点表示了阶跃点的取值。
图1 用不同的方法将小数舍入到整数
从无限精度到某个浮点数格式进行舍入的基本流程:
如果输入浮点数的指数大于在目标格式表示:
- 如果输入浮点数超越了目标格式的表示范围,那么输入浮点数无法用目标格式表示。
舍入方法 | 小于目标格式的最小负规范数 | 大于目标格式的最大正规范数 |
---|---|---|
RM | 负无穷 | 最大合规正数 |
RP | 最小合规负数 | 正无穷 |
RZ | 最小合规负数 | 最大合规正数 |
RN | 负无穷 | 正无穷 |
RA | 负无穷 | 正无穷 |
RO | 最小合规负数 | 最大合规正数 |
如果输入浮点数的指数能够在目标格式表示,那么根据目标格式的尾数宽度,将输入浮点数的尾数范围高低两部分,分别用A和B表示。比如从DP到SP转换,则A是[51:29],B是[28:0]。在目标格式下,距离输入浮点数最近的两个浮点数的尾数是A和A+1。
舍入方法是RM:
- 当B!=0而且符号数是负数时,结果的尾数选择A+1,即正浮点数舍去。
- 否则,结果的尾数是A,即负浮点数舍入。
舍入方法是RP:
- 当B!=0而且符号数是正数时,结果的尾数选择A+1,即正浮点数舍入。
- 否则,结果的尾数是A,即负浮点数舍去。
- 舍入方法是RZ:所有情况下都舍去,结果的尾数是A。
舍入方法是RN:
如果B只有最高位为1,其他比特为0,则需要进一步比较A:
- 如果A是偶数,那么选择A作为结果的尾数;
- 如果A是奇数,那么选择A+1作为结果的尾数,即舍入到偶数。
- 如果B的最高位为1,而且其他比特不为1,那么选择A+1作为结果的尾数。
- 否则,选择A作为结果的尾数。
舍入方法是RA:
- 如果B只有最高位为1,其他比特为0,那么选择A+1作为结果尾数,即舍入到绝对值更大的浮点数。
- 如果B的最高位为1,而且其他比特不为1,那么选择A+1作为结果的尾数。
- 否则,选择A作为结果的尾数。
- 如果舍入方法是RO:以A作为结果浮点数的尾数,并且保证尾数的最低为是1,即A|0x1。
最后,组合符号位、指数和尾数,得到目标格式的结果浮点数。
- 结果浮点数的符号位与输入浮点数的符号位相同。
- 结果浮点数的指数与输入浮点数的指数相同。需要根据目标格式的偏置计算指数域编码。
- 结果浮点数的尾数按照上一步的规则计算。
上述流程中没有考虑如何处理非规范数、无穷大和NaN的输入。
舍入误差的简单分析
为了进一步说明不同舍入方法的区别,需要对不同舍入方法引入的误差进行分析。受限于数学知识和计算能力,这里对于分析过程进行了一些假设和简化。
误差分析的假设
对于误差分析,我们提出了下列假设:
- 选择从SP到HP的舍入过程进行分析。
- 忽略了无法被HP表示的SP范围,而且不考虑非规范数。
- 只考虑浮点值[1.0, 2.0)的部分,即指数等于偏执的SP浮点数。指数等于其他值的时候,其规律与[1.0, 2.0)的部分相同。
- 只考虑正浮点数的分析过程,称为正半幅;同时考虑正和负浮点数的分析过程,称为全幅。
根据这三个假设,我们不需要考虑全部232个浮点数。正半幅的误差分析需要考虑223个浮点数,全幅的误差分析需要考虑2 x 223个浮点数。
舍入误差分析
在本节中度量累计误差和累计绝对误差作为评价指标,前者直接累加误差;后者累加误差的绝对值。下表提供了六种舍入方法的误差指标。
舍入方法 | 正半幅累计误差 | 正半幅累计绝对误差 | 全幅累计误差 | 全幅累计绝对误差 |
---|---|---|---|---|
RM | -4095.5 | 4095.5 | -8191.0 | 8191.0 |
RP | 4095.5 | 4095.5 | 8191.0 | 8191.0 |
RZ | -4095.5 | 4095.5 | 0.0 | 8191.0 |
RN | 0.0 | 2048.0 | 0.0 | 4096.0 |
RA | 0.5 | 2048.0 | 0.0 | 4096.0 |
RO | 0.0 | 4095.5 | 0.0 | 8191.0 |
结合前面的图以及上表,我们可以对于舍入方法的行为进行更加详细的解释。
RM、RP和RZ可以归纳为同一类舍入方法。这三种方法的阶跃点都处于目标格式能够精确表示的值上,比如上图中的0、+/-1、+/-2、+/-3等位置。对于处于目标格式的两个相邻值之间的浮点数,这三种方法都是提供相同方向的误差,因此三种舍入方法的累计绝对误差相同,但是累计误差不同。RM总是引入负的误差,所以其累计误差都是负数;RP总是引入正的误差,所以其累计误差都是正的。对于负数,RZ引入负的误差;对于正数,RZ引入正的误差,所以整数和负数引入的误差相互抵消,全幅累计误差是0。
RN和RA两种方法比较类似,阶跃点处于目标格式的两个相邻数值中间的位置,比如上图中+/-0.5、+/-1.5、+/-2.5等位置。在两个相邻数值之间,一半引入正向误差(比如区间(0.5,1)),另一半引入负向误差(比如区间(0,0.5))。所以,RN和RA引入的累计误差很小,而且引入绝对误差也比RM/RP/RZ引入的误差较小。对于阶跃点,RA总是舍入,这就导致对于正数引入正向误差,对于负数引入负向误差。如上表所示,RA引入的正半幅累计误差是0.5,这个值虽然远小于RM/RP/RZ引入的累计误差,但是仍然意味着固定方向的误差。RN对于阶跃点不会一味舍入,而是一半舍入一半舍去。如上表所示,RN引入的累计误差是0。这就表示,对于更加复杂的需要多步操作完成的计算过程,RN可以在更长的计算步骤上保证精确。
Round-to-Odd
RO与上面五种舍入方法都不太一样。RO的阶跃点处于目标格式的偶数位置上,比如上图中的0、+/-2、+/-4等位置。RO的累计绝对误差与RM/RP/RZ相同。在两个阶跃点之间,RO总是引入一半正向误差(比如区间(0,1)),一半负向误差(比如区间(1,2)),相互抵消,这就导致累计误差为0。
引入RO的目的是为了在连续多次格式转换时保持准确性。当需要多次舍入才能得到需要的目标格式是,前面多次转换使用RO,最后一次转换使用需要的舍入方式。
为什么需要引入RO
以DP到HP进行转换为例,输入DP是0x4000020010000000(2.0009766817092896)。
使用RN将DP直接转换到HP。尾数[51:42]为0,尾数[41:0]是0x20010000000。由于尾数[41:0]大于阶跃点,距离绝对值更大的浮点数更近,所以结果尾数是0x1,结果HP是0x4001(2.001953125),误差是0.0009764432907104492。
由于某种限制,可能需要先从DP转换到SP,再从SP转换到HP。首先使用RN从DP到SP进行转换。尾数[51:29]为0x1000,尾数[28:0]是0x10000000。由于尾数[28:0]处于阶跃点,所以RN固定舍入到偶数,得到的尾数是0x1000,结果SP是0x40001000(2.0009765625)。
再使用RN从SP到HP进行转换。尾数[22:13]为0,尾数[12:0]是0x1000。由于尾数[12:0]也是处于阶跃点,所以还是舍入到偶数,得到的HP是0x4000(2.0),误差是-0.0009766817092896。
可见,两次转换得到了错误的结果,误差更大。而出现这样原因在于,第一次舍入后得到的中间结果到两个浮点数的距离相同,因此舍入方向不取决于输入浮点数,而是固定舍入到偶数,从而导致舍入方向与预期不同。
RO固定将目标格式的尾数最低位设置为1,使得中间结果一定不会处于两个浮点数之间。从而避免了这个问题。仍然分析上面的例子:
首先使用RO从DP到SP进行转换。尾数[51:29]为0x1000,尾数[28:0]是0x10000000。舍去尾数[28:0],并且强制尾数[29]为1,得到的尾数是0x1001,结果SP是0x40001001(2.000976800918579)。再使用RN从SP到HP进行转换。尾数[22:13]为0,尾数[12:0]是0x1001。由于尾数[12:0]距离绝对值更大的浮点数更近,得到尾数是0x1,结果HP是0x4001(2.001953125)。这个结果与预期相同。
Arm架构中的舍入模式配置
浮点指令(Advsimd、SVE和SME指令)使用的舍入方法由FPCR.RMode
域设置。这个域包含四种取值:
FPCR.RMode | 含义 |
---|---|
0b00 | 舍入到最近(RN) |
0b01 | 舍入到正无穷(RP) |
0b10 | 舍入到负无穷(RM) |
0b11 | 舍入到零(RZ) |
还有一些指令不受FPCR.RMode
的控制:
当
PFCR.AH
是1时,下列指令固定使用RN舍入模式,不受FPCR.RMode
控制:FRECPE
、FRECPS
、FRECPX
、FRSQRTE
和FRSQRTS
。- Bfloat16乘加指令:
BFML[A|S]L[B|T]
。 - Bfloat16转换指令:
BFCVT
、BFCVTN
、BFCVTN2
和BFCVTNT
。
- FP8指令默认使用RN模式,不受
FPCR.RMode
控制。 Arm提供了使用特定舍入方式的转换指令(CVT)和舍入指令(FRINT)。
- 固定使用RN模式:
FRINTN
- 固定使用RP模式:
FCVTP[U|S]
,FRINTP
- 固定使用RM模式:
FCVTM[U|S]
,FRINTM
- 固定使用RZ模式:
FCVTZ[U|S]
,FJCVTZS
,FRINT[32|64]Z
和FRINTZ
- 固定使用舍入向外的模式:
FCVTAS
,FCVTAU
和FRINTA
。
- 固定使用RN模式:
舍入到奇数只在下面两种场景使用:
- 当
FPCR.EBF
是0时,BFDOT
、BFMMLA
、BFMOPS
指令一定使用RO舍入模式。 FCVTXN
、FCVTXN2
、FCVTX
和FCVTXNT
固定使用RO模式,避免二次舍入的误差。
上一篇:Arm浮点特性(一)浮点数格式
下一篇:Arm浮点特性(三)浮点数运算操作