【发布时间】:2021-09-27 10:05:49
【问题描述】:
我在 ARMv8 环境中使用双精度数据类型进行编码。当给定优化选项-O3时,用C语言实现的值与在Assembly(NEON)中使用ARMv8指令的值不同。单独使用 FMUL 和 FADD 时,取值相同,但同时使用 FMUL + FADD 时,结果值与 C 语言不同。我们想解决这个问题。
这里是汇编文件
.data
.text
.global Asm_Operation_Test
Asm_Operation_Test:
MOV x3,#0
Operation_Loop:
LD1 {v0.2d-v3.2d},[x0],#64
LD1 {v4.2d-v7.2d},[x1],#64
FMUL v0.2d,v0.2d,v4.2d
FADD v0.2d,v0.2d,v4.2d
FMUL v1.2d,v1.2d,v5.2d
FADD v1.2d,v1.2d,v5.2d
FMUL v2.2d,v2.2d,v6.2d
FADD v2.2d,v2.2d,v6.2d
FMUL v3.2d,v3.2d,v7.2d
FADD v3.2d,v3.2d,v7.2d
ST1 {v0.2d-v3.2d},[x2],#64
ADD x3,x3,#1
CMP x3,#32
BNE Operation_Loop
ret
这里是 C 文件
typedef struct {double v;} fpr;
C_Operation Test(fpr*a, fpr*b, fpr*c){
for(int i=0; i<256; i++)
{
c[i].v = a[i].v * b[i].v + b[i].v;
}
}
汇编函数和 C 函数执行相同的操作。输入数据为double类型,输入256个随机数(double)的数组。如果加上 gcc -O0 选项,这两个函数的结果是完全一样的。但是执行gcc-O3时,两个函数的结果值并不完全相同,只有12位十进制与单精度相同。我们想知道其中的原因。
我们的比较功能很简单。 if( ((double) a[i].v != (double)b[i].v)) printf("Error\n")
【问题讨论】:
-
您能否添加有关该问题的更多详细信息?例如,您能否展示问题中提到的 C 代码行、汇编行和值以及使用的编译器?
-
使用FMA指令之类的声音。检查编译器供应商提供的文档。可能有一个选项可以强制对所有子表达式进行正确舍入。
-
我附上了代码和其他解释。我们也使用 Jetson Xavier (ARMv8.2),我们使用 gcc 7.5.0。我们确认 ARMv8 浮点指令集支持双精度。
-
C 标准不要求 C 运算符和 IEEE-754 浮点算术运算之间直接对应,也不要求在计算浮点表达式时使用标称精度。在 C 中,
a*b + c可以编译为a和b的乘法(四舍五入到类型中可表示的最接近的值),然后是c的加法(也四舍五入)或融合乘法- add(计算单个结果 a•b+c 就好像最后只有一个舍入)。此外,float表达式可以使用double类型等计算。 -
因此,如果您出于某种原因想要将您的程序集与 C 代码相匹配,您可以编译 C 代码并向编译器发出请求以显示生成的程序集(在 GCC 和 Clang 中为
-S) 然后重写您的程序集以匹配。但通常没有理由这样做。您要解决的实际问题是什么?您真的需要程序集与 C 完全匹配吗?为什么?还是您担心不匹配表明计算中存在一些错误?
标签: assembly floating-point precision neon armv8