本文将描述x86与ARM两大处理器架构中的浮点控制寄存器以及对某些标志位的置1跟清零的不同行为。
Intel软件开发者指南10.2.3小节(MXCSR Control and Status Register)中详细介绍了MXCSR寄存器每个标志位的布局以及含义。下面对主要的几个控制字段进行讲解:
该标志控制了对一个SIMD浮点下溢条件的响应。所谓下溢是指,当计算结果的指数部分为全零,而尾数部分不为全零的情况下,该浮点数会作为IEEE754标准中的非规格化数。当下溢异常被屏蔽而flush-to-zero标志被置1时,处理器在它检测到下溢情况发生时会做以下操作:
flush-to-zero模式不与IEEE754标准兼容,IEEE所托管的对下溢情况的响应是将该结果作为一个非规格化数(denormalized result)(参考4.8.3.2 “Normalized and Denormalized Finite Numbers”这一小节)。此标志的引入主要是为了提升性能。如果我们的应用允许这么一点精度丢失而获得更快执行性能的话,我们可以把此标志位设置为1。默认情况下,此标志为0,即禁用flush-to-zero模式。
下面列出开启/禁用该标志之后对非规格化数据做单精度浮点加法之后的结果
src1数据 | src2数据 | 开启FTZ结果 | 禁用FTZ结果 |
---|---|---|---|
0x8040’0000 | 0x8040’0000 | 0x80800000 | 0x80800000 |
0x8008’0000 | 0x8008’0000 | 0x80000000 | 0x80100000 |
从上表数据我们可以看到,当开启FTZ之后,如果计算结果产生一个非规格化数,那么该数将会被清零,但保留其原始的符号位。而如果两个非规格化数进行算术运算之后的结果不产生一个非规格化数,那么结果保持不变。
关于此字段,详情看见《x86_64与ARM64的signaling NaN与Quiet NaN,以及浮点数的舍入模式》。
这个标志位允许x86处理器开启“非规格化数为零”模式。当此标志被置1时,处理器会在做计算操作之前,将所有非规格化的源操作数转换为零,而符号位则保留原始值的符号。此时处理器不会设置非规格化操作数异常标志(DE),不管我们有没有设置非规格化异常屏蔽位(DM)。
denormals-are-zeros模式不与IEEE754标准兼容,它用于提升处理器执行的性能,比如应用于流媒体处理,在这些应用下将非规格化操作数舍入到零不会影响处理后数据的质量。
下面列出开启/禁用该标志之后对非规格化数据做单精度浮点加法之后的结果:
src1数据 | src2数据 | 开启DAZ结果 | 禁用DAZ结果 |
---|---|---|---|
0x8040’0000 | 0x8040’0000 | 0x80000000 | 0x80800000 |
0x8008’0000 | 0x8008’0000 | 0x80000000 | 0x80100000 |
从上表中我们确实可以看到,结果与FTZ的很类似,唯独在开启DAZ标志之后做单精度浮点加法的结果两者不一样。因为在开启了DAZ之后,两个非规格化数都被舍入到-0,因此两个-0相加结果也是-0。
这里需要再提一点的是,如果一个 -0
与一个 +0
相加,那么结果为需要看当前RC位所指定的舍入模式:
+0
。-0
。在ARMv8架构参考指南中的C5.2.7小节(FPCR, Floating-point Control Register)中详细描述了ARMv8架构下在64位执行模式中浮点控制寄存器的使用以及执行效果。
此标志位用于控制半精度浮点采用IEEE754-2008标准还是ARM设计的一个替代实现。当此标志为0时,采用IEEE754-2008标准的半精度表示法;而为1时,则采用ARM自己设计的替换表示。
这里需要注意的是,从ARMv8.2所引入的新增FP16指令扩展会直接采用IEEE格式的半精度浮点,而直接忽略此标志。
下面简单介绍一下ARM自己设计的半精度浮点表示。在ARMv8参考指南中的A1.4.2小节(Half-precision floating-point formats)有所详细描述。它与IEEE754-2008的规范一样,1位符号位;5位指数位;10位尾数位。但在表示无穷大以及非数上两者表示有差异。
该标志控制着是否将一个NaN操作数进行传播到浮点操作的输出,即即便有某个操作数为NaN,计算照常完整执行。如果该标志置1,那么对于一条浮点运算操作指令,只要有某一操作数为NaN,则立即返回ARM处理器所设定好的一个默认NaN值。这个标志同样可以起到提升浮点数执行性能。
如果DN标志为1,那么对于三种浮点类型的默认NaN数值如下:
该模式的行为效果与x86的 DAZ 标志一致(而不是 FTZ )——如果该标志被置1,那么对于一条浮点计算指令,操作数中所有非规格化数全都被舍入为0。另外,此标志仅仅控制32位单精度浮点与64位双精度浮点,而不对ARMv8.2-FP16所引入的16位半精度浮点产生影响。
关于此字段,详情看见《x86_64与ARM64的signaling NaN与Quiet NaN,以及浮点数的舍入模式》。
该标志只有当ARM架构支持了ARMv8.2-FP16指令扩展之后才有效。该标志是对半精度浮点的flush-to-zero操作的控制,语义与上面的FZ一样。只不过此标志仅仅用于控制16位半精度浮点,而对其他浮点类型不受影响。
在ARMv7架构参考指南中的B4.1.58小节(FPSCR, Floating-point Status and Control Register, VMSA)中描述了关于此浮点状态控制寄存器的相关信息。ARMv7中的FPCSR其实就相当于ARMv8的64位执行模式下的FPCR与FPSR的组合。它将控制标志与状态标志合成在了一个寄存器中。因此它所对应的浮点控制标志与上面所描述的FPCR中控制标志基本一致。
这里ARMv7与ARM64不同的是,浮点与SIMD操作有自己独立的NZCV标志域。这就意味着当我们执行了一条 VCMP
浮点比较指令之后,如果想根据当前更新的NZCV标志做条件执行,需要使用 VMRS
指令将FPCSR的NZCV拷贝到APSR状态寄存器中。
此外,FPCSR还有一个QC(比特27)标志,它是累加饱和标志位,并且仅适用于SIMD指令操作。如果某一条SIMD整数指令操作产生了对结果的饱和,那么该标志位被置1。