我在网上找到了一些建议。
我有类似的问题,但没有一个建议有帮助(或者我没有正确地弄清楚如何根据我的程序实现它们)。
代码作为asm(...)
插入到C程序中。
使用-masm=intel
进行编译后,使用:
asm ("FLD EBX \n" "FSQRT \n" "FST EBX \n").
我收到编译错误:
"错误:' fld'"的操作数类型不匹配 " ...' fst'"。
不匹配EBX在这些命令之前保留一些整数正值。
那么获取ebx = sqrt(ebx)的正确方法是什么?
答案 0 :(得分:4)
您应该在现代代码中使用SSE / SSE2作为sqrt,而不是x87。您可以使用一条指令直接将gp寄存器中的整数转换为xmm寄存器中的double。
cvtsi2sd xmm0, ebx
sqrtsd xmm0, xmm0 ; sd means scalar double, as opposed to SIMD packed double
cvttsd2si ebx, xmm0 ; convert with truncation (C-style cast)
; cvtsd2si ecx, xmm0 ; rounded to nearest integer (or whatever the current rounding mode is)
这也适用于64位整数(rbx
),但请注意double
只能精确表示最多约2 ^ 53(尾数大小)的整数。如果要检查整数是否为正方形,可以使用float sqrt然后对整数结果进行试验乘法。 ((a*a) == b
)
有关指南,教程和手册的链接,请参阅x86。
请注意,将此代码插入C程序的中间是完全错误的方法。 GNU C inline asm是执行asm的最难的方法,因为你必须真正理解所有内容以使约束正确。让他们错误可能导致其他周围的代码以微妙和难以调试的方式破解,而不仅仅是你在内联错误中所做的事情。有关此内容的更多详细信息,请参阅x86标记wiki。
如果你想要int a = sqrt((int)b)
,那么在你的代码中写下它,让编译器为你生成这三条指令。通过各种方式阅读和理解编译器输出,但不要盲目地在asm("")
的中间插入一个序列。
e.g:
#include <math.h>
int isqrt(int a) { return sqrt(a); }
compiles to(gcc 5.3不带-ffast-math):
pxor xmm0, xmm0 # D.2569
cvtsi2sd xmm0, edi # D.2569, a
sqrtsd xmm1, xmm0 # tmp92, D.2569
ucomisd xmm1, xmm1 # tmp92, tmp92
jp .L7 #,
cvttsd2si eax, xmm1 # D.2570, tmp92
ret
.L7:
sub rsp, 8 #,
call sqrt #
add rsp, 8 #,
cvttsd2si eax, xmm0 # D.2570, tmp92
ret
我猜sqrt()
必须在某些错误上设置errno。 :/
使用-fno-math-errno
:
pxor xmm0, xmm0 # D.2569
cvtsi2sd xmm0, edi # D.2569, a
sqrtsd xmm0, xmm0 # tmp92, D.2569
cvttsd2si eax, xmm0 # D.2570, tmp92
ret
pxor
是为了打破对xmm0之前内容的错误依赖,因为cvtsi2sd
做出了奇怪的设计决定,使dest向量reg的上半部分保持不变。只有在您希望将转换结果插入现有向量时,这才有用,但已经cvtdq2pd
进行了压缩转换。 (并且它们可能没有考虑到64位整数,因为当英特尔发布SSE2时,AMD64仍然处于绘图板上。)