我在网上找到了一些建议。
我有一个类似的问题,但是所有建议都没有帮助(或者我没有根据我的程序正确弄清楚如何实现它们)。
该代码作为
asm(...)
插入到C程序中。使用
-masm=intel
编译后,使用时:asm ("FLD EBX \n" "FSQRT \n" "FST EBX \n").
我收到编译错误:
“错误:'fld'的操作数类型不匹配”
“...'fst'不匹配”。
在这些命令之前,EBX持有一些整数正值。
那么获取ebx = sqrt(ebx)的正确方法是什么?
最佳答案
您应该在现代代码中将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内联汇编是执行汇编的最困难的方法,因为您必须真正了解所有内容才能正确地执行约束。弄错它们可能导致其他周围的代码以微妙且难以调试的方式破坏,而不仅仅是您在处理内联汇编错误时所做的事情。有关更多信息,请参见x86标签Wiki。
如果需要
int a = sqrt((int)b)
,则将其写入代码中,然后让编译器为您生成这三个指令。一定要阅读并理解编译器的输出,但不要仅仅使用asm("")
盲目地将其序列放入中间。例如。:
#include <math.h>
int isqrt(int a) { return sqrt(a); }
compiles to(不带-ffast-math的gcc 5.3):
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 vector reg的上半部分。仅在要将转换结果插入到现有 vector 中时才有用,但是已经有cvtdq2pd
进行打包转换。 (而且他们可能没有考虑64位整数,因为当Intel发布SSE2时AMD64仍处于起步阶段)。
关于assembly - 组装x86中的Sqrt,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35747811/