我在64位系统中使用FLD指令遇到了一点小问题... 想要将双精度浮点数加载到堆栈指针FPU的st0寄存器中,但似乎无法实现。 在Delphi x32中,我可以使用以下代码:
function DoSomething(X:Double):Double;
asm
FLD X
// Do Something ..
FST Result
end;
很不幸,在x64中,相同的代码无法运行。
var
作为解决方法,例如:function DoSomething(var X:Double):Double;
asm
FLD qword ptr [X]
// Do Something ..
FST Result
end;
function DoSomething(X:Double):Double;
var
Temp : double;
asm
MOVQ qword ptr Temp,X
FLD Temp
//do something
FST Temp
MOVQ xmm0,qword ptr Temp
end;
在x86-64代码中,您不需要使用传统的x87堆栈寄存器,因为SSE2是基线,是x86-64 ISA的必需部分。 您可以并且应该使用{{link1:addsd
}},mulsd
,sqrtsd
等XMM寄存器上的标量FP数学运算。 (或者对于float使用addss
)
如果它们是函数的前四个参数之一,则Windows x64调用约定将float / double FP参数传递到XMM0..3中。 (即第3个总参数进入xmm2,如果它是FP,则第3个FP参数进入xmm2。)它返回XMM0中的FP值。
仅在实际需要函数内部的80位精度时才使用x87。 (诸如fsin
和fyl2x
之类的指令不快,并且通常可以通过使用SSE / SSE2指令的普通数学库来完成。)
function times2(X:Double):Double;
asm
addsd xmm0, xmm0 // upper 8 bytes of XMM0 are ignored
ret
end
将数据存储到内存并重新加载到x87寄存器中会浪费大约10个时钟周期,而没有任何好处。SSE / SSE2标量指令与它们的x87等效指令一样快,甚至更快,并且更易于编程和优化,因为您永远不需要使用fxch
; 它是基于平面寄存器设计而不是基于堆栈的(https://agner.org/optimize/)。此外,您还有15个XMM寄存器。
float
/double
上使用SSE2进行数学计算,因此这是要比较的标准。此外,一些32位编译器(特别是MSVC)将x87单元设置为64位精度(53位尾数),以更接近C FLT_EVAL_METHOD=1语义,因此如果您使用该实现,那么额外的精度也不会存在。 - Peter Cordesdouble
的精度已经足够了。对于一些问题,通过仔细的数值设计,可以使用32位的float
来每个SIMD指令完成2倍的工作量。如果你真的关心FP舍入误差,你可以采取一些措施,比如Kahan求和来补偿数组求和时的误差,或者是成对求和。使用多个SIMD累加器展开是朝着这个方向迈出的一步,通常可以减少舍入误差。 - Peter Cordes
Extended
类型。这表明 Delphi Win64 不使用 FPU(x86),而是使用 SSE。因此,使用 FPU 指令会有问题。同时,在使用 BAsm x64 时要小心——存在会破坏数据甚至导致程序控制流反转的错误。 - Arioch 'The