vfmaq_f32是否具备更高运行精度?不同架构及编译选项影响探究
vfmaq_f32 是否真的具备更高运行精度?
问题描述
vfmaq_f32真的具备更高的运行精度吗?笔者推测其精度取决于不同架构中浮点处理单元的位扩展长度,在macOS ARM64架构下运行代码结果一致。请问在其他架构上能否得到更高或更低精度的结果?是否存在可控制结果精度的编译选项?
测试代码:
#include <arm_neon.h> #include <iostream> using namespace std; int main(){ float a = 12.3839467819; float b = 21.437678904; float c = 4171.42144; printf("%.17f\n",a); printf("%.17f\n",b); printf("%.17f\n",c); printf("%.17f\n",a+b*c); float32x4_t a_reg = vdupq_n_f32(a); float32x4_t b_reg = vdupq_n_f32(b); float32x4_t c_reg = vdupq_n_f32(c); float32x4_t res_reg = vfmaq_f32(a_reg, b_reg, c_reg); float res[4] = {0.f}; vst1q_f32(res,res_reg); printf("%.17f\n",res[0]); res_reg = vmlaq_f32(a_reg, b_reg, c_reg); vst1q_f32(res,res_reg); printf("%.17f\n",res[0]); res_reg = vmulq_f32(b_reg, c_reg); res_reg = vaddq_f32(res_reg, a_reg); vst1q_f32(res,res_reg); printf("%.17f\n",res[0]); return 0; }
解答
1. vfmaq_f32 的精度本质
vfmaq_f32是ARM NEON的**融合乘加(FMA)**指令,理论上的精度优势来自:它会把乘法的中间结果保留在浮点单元的扩展精度寄存器中(比如部分ARM架构支持64/80位扩展精度),直接与累加数相加后再截断为32位float;而vmulq_f32 + vaddq_f32的组合是先把乘法结果截断为32位,再执行加法,多了一次精度损失。
你在macOS ARM64上结果一致,是因为Apple Silicon的FPU处理单精度FMA时,全程以32位float精度完成操作,没有启用扩展精度存储中间值,所以和分开乘加的结果无差异。
2. 不同架构下的精度差异
- 非Apple的ARMv7-A/ARMv8-A架构:比如高通、三星的部分SoC,其FPU支持单精度乘加时用64/80位扩展精度存储中间值,此时
vfmaq_f32的结果会比分开乘加更精确,误差更小。 - 低功耗ARM核心(如Cortex-M系列):部分轻量FPU不支持扩展精度,
vfmaq_f32和分开乘加的精度完全一致。 - x86/x86_64平台(交叉编译仿真NEON):仿真后的FMA精度取决于编译器实现,x86原生的FMA指令(如
vfmaddps)本身有类似精度特性,但仿真结果可能和原生ARM有差异。
3. 控制精度的编译选项
主流编译器提供以下选项来约束浮点运算精度:
- GCC/Clang:
-ffp-contract=fast:默认开启,允许编译器自动将普通乘加代码优化为FMA指令,可能改变运算精度。-ffp-contract=off:禁止编译器自动融合乘加,确保分开的乘加操作不会被优化为FMA,但直接调用vfmaq_f32这类显式FMA指令不受影响。-mfpmath=387(仅x86):强制使用80位x87浮点单元,提升单精度运算的中间精度,不适用于ARM。
- ARM Compiler (armclang):
-ffp-contract=fast/off:作用同GCC;另外可通过-mfpu=neon-fp-armv8等选项指定FPU版本,部分版本会强制特定精度模式。
内容的提问来源于stack exchange,提问作者gaoshuzhendanteb
相关产品推荐
相关产品推荐

