如何:x86中的pow(真实,真实)

时间:2011-01-09 09:18:12

标签: x86 assembly pow

我正在寻找x86 Assembly中pow(real, real)的实现。另外,我想了解算法的工作原理。

3 个答案:

答案 0 :(得分:62)

只需将其计算为2^(y*log2(x))

有一个x86指令FYL2X来计算y * ​​log2(x)和一个x86指令F2XM1来进行求幂。 F2XM1需要[-1,1]范围内的参数,因此您必须在两者之间添加一些代码以提取整数部分和余数,对余数进行取幂,使用FSCALE以适当的2的幂来缩放结果。

答案 1 :(得分:16)

好的,我按照你的建议在x86中实现了power(double a, double b, double * result);

代码:http://pastebin.com/VWfE9CZT

%define a               QWORD [ebp+8]
%define b               QWORD [ebp+16]
%define result          DWORD [ebp+24]
%define ctrlWord            WORD [ebp-2]
%define tmp             DWORD [ebp-6]

segment .text
    global power

power:
    push ebp
    mov ebp, esp
    sub esp, 6
    push ebx

    fstcw ctrlWord
    or ctrlWord, 110000000000b
    fldcw ctrlWord

    fld b
    fld a
    fyl2x

    fist tmp

    fild tmp
    fsub
    f2xm1
    fld1
    fadd
    fild tmp
    fxch
    fscale

    mov ebx, result
    fst QWORD [ebx]

    pop ebx
    mov esp, ebp
    pop ebp
    ret

答案 2 :(得分:1)

这是我使用“ The Svin”算法的主要功能。我将其包装在__fastcall和__declspec(naked)装饰中,并添加了代码以确保base / x是正数。如果x为负,则FPU将完全失败。您需要检查“ x”符号位,再考虑“ y”的奇/偶数位,并在符号完成后应用它!莱姆知道任何随机读者的想法。如果可能的话,寻找带有x87 FPU代码的更好版本。由于各种原因,它始终可以使用Microsoft VC ++ 2005进行编译/工作。

// Main Source: The Svin
// pow(x,y) is equivalent to exp(y * ln(x))

extern double __fastcall fs_Power(double x, double y);

__declspec(naked) double __fastcall fs_Power(double x, double y) { __asm {
    LEA   EAX, [ESP+12]        ;// Save 'y' index in EAX
    FLD   QWORD PTR [EAX]      ;// Load 'y' (exponent) (works positive OR negative!)
    FIST  DWORD PTR [EAX]      ;// Round 'y' back to INT form to test for odd/even bit
    MOV   AX, WORD PTR [EAX-1] ;// Get left sign bit of 'x' AND right odd/even bit from 'y'!!
    FLD   QWORD PTR [ESP+4]    ;// Load 'x' (base) (make positive!)
    AND   AX, 0180h  ;// AND off all bits except right 'y' odd bit AND left 'x' sign bit!
    FABS    ;// 'x' MUST be positive, BUT check sign/odd bits pre exit!
    FYL2X   ;// 'y' * log2 'x'
    FLD1    ;// Load 1.0f
    FLD   ST(1)
    FPREM   ;// Partial remainder
    F2XM1   ;// 2 ^ X - 1
    FADD
    FSCALE  ;// Scale by factor of 2
    FSTP  ST(1)
;// Final task, make result negative if needed!
    CMP   AX, 0180h     ;// Is 'y' odd bit AND 'x' sign bit set [combo-test]?
    JNE   EXIT_RETURN   ;// If positive, exit; if not, add '-' sign!
        FCHS            ;// 'x' is negative, 'y' is ~odd, final result = negative! :)
EXIT_RETURN:
;// For __fastcall/__declspec(naked), gotta clean stack here (2 x 8-byte doubles)!
    RET   16     ;// Return & pop 16 bytes off stack
}}

好的,为了完成本实验,我使用RDTSC CPU时间戳/时钟计数器指令进行了基准测试。我遵循将“ SetPriorityClass(GetCurrentProcess(),HIGH_PRIORITY_CLASS);”设置为“高优先级”的建议。然后我关闭了所有其他应用程序。

结果:我们的复古x87 FPU数学函数“ fs_Power(x,y)”比MSCRT2005 pow(x,y)版本快50-60%,后者使用一个相当长的SSE代码分支,标记为“ _pow_pentium4:”,如果它检测到64位>奔腾4+ CPU。所以啊! :-)

注意:(1)CRT pow()有一个〜33微秒的初始化分支,它在此测试中显示为46,000。 1200至3000个周期后,它以正常平均值运行。我们手工制作的x87 FPU外观精美,运行稳定,首次通话不会造成任何惩罚!

(2)尽管CRT pow()丢失了所有测试,但它却在一个区域中获胜:如果输入了野外的,巨大的,超出范围的值/溢出值,它将迅速返回错误。由于大多数应用通常/正常使用都不需要进行错误检查,因此这是无关紧要的。

https://i.postimg.cc/QNbB7ZVz/FPUv-SSEMath-Power-Proc-Test.png

第二次测试(我必须再次运行它才能在图像捕捉后复制/粘贴文本):

 x86 fs_Power(2, 32): CPU Cycles (RDTSC): 1248
MSCRT SSE pow(2, 32): CPU Cycles (RDTSC): 50112

 x86 fs_Power(-5, 256): CPU Cycles (RDTSC): 1120
MSCRT SSE pow(-5, 256): CPU Cycles (RDTSC): 2560

 x86 fs_Power(-35, 24): CPU Cycles (RDTSC): 1120
MSCRT SSE pow(-35, 24): CPU Cycles (RDTSC): 2528

 x86 fs_Power(64, -9): CPU Cycles (RDTSC): 1120
MSCRT SSE pow(64, -9): CPU Cycles (RDTSC): 1280

 x86 fs_Power(-45.5, 7): CPU Cycles (RDTSC): 1312
MSCRT SSE pow(-45.5, 7): CPU Cycles (RDTSC): 1632

 x86 fs_Power(72, -16): CPU Cycles (RDTSC): 1120
MSCRT SSE pow(72, -16): CPU Cycles (RDTSC): 1632

 x86 fs_Power(7, 127): CPU Cycles (RDTSC): 1056
MSCRT SSE pow(7, 127): CPU Cycles (RDTSC): 2016

 x86 fs_Power(6, 38): CPU Cycles (RDTSC): 1024
MSCRT SSE pow(6, 38): CPU Cycles (RDTSC): 2048

 x86 fs_Power(9, 200): CPU Cycles (RDTSC): 1152
MSCRT SSE pow(9, 200): CPU Cycles (RDTSC): 7168

 x86 fs_Power(3, 100): CPU Cycles (RDTSC): 1984
MSCRT SSE pow(3, 100): CPU Cycles (RDTSC): 2784

任何现实世界中的应用程序?是! Pow(x,y)大量用于帮助将CD的WAVE格式编码/解码为OGG,反之亦然!当您编码完整的60分钟的WAVE数据时,节省时间的收益将是很重要的! OGG / libvorbis中使用了许多数学函数,例如acos(),cos(),sin(),atan(),sqrt(),ldexp()(非常重要)等。因此,像这样的微调版本无需打扰/不需要错误检查,可以节省很多时间!

我的实验是为NSIS安装程序系统构建OGG解码器的结果,这导致我用上面看到的内容替换了算法所需的所有Math“ C”库函数。好吧,ALMOST,我在x86中需要acos(),但是我仍然找不到任何东西...

致谢,希望这对喜欢修补的其他人有用!