我正在寻找实施 pow(real, real) 在X86组件中。我也想了解该算法的工作原理。

有帮助吗?

解决方案

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

有一个x86指令fyl2x来计算y*log2(x)和x86指令f2xm1进行指控。 F2XM1需要在[-1,1]范围内的参数,因此您必须在之间添加一些代码才能提取整数部分和剩余的零件,并指定其余部分,使用FSCALE来扩展结果2。

其他提示

好,我实施了 power(double a, double b, double * result); 如您所建议,在X86中。

代码: 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

这是我使用“ SVIN”的主算法的功能。我将其包裹在__fastCall&__declspec(裸)装饰中,并添加了代码以确保基本/x是正面的。如果X为负,则FPU将完全失败。您需要检查“ X”标志位,并考虑“ Y”的奇数/偶数,并在完成后应用! Lemme知道您对任何随机读者的想法。如果可能的话,请使用X87 FPU代码寻找更好的版本。它与Microsoft VC ++ 2005合作,出于各种原因,我一直坚持使用的内容。

兼容性诉ANSI POW(X,Y):很好!更快,可预测的结果,负值处理,只是无效输入的错误反馈。但是,如果您知道“ y”总是可以是int/long,请不要使用此版本。我发布了Agner Fog的版本,并避免了非常慢的FSCALE,请搜索我的个人资料!在那些有限的情况下,他是最快的X87/FPU方式!

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

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

__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
    MOVZX EAX, WORD PTR [EAX-1] ;// Get x's left sign bit AND y's right odd/even bit!
    FLD   QWORD PTR [ESP+4]     ;// Load 'x' (base) (make positive next!)
    FABS            ;// 'x' MUST be positive, BUT check sign/odd bits pre-exit!
    AND   AX, 0180h ;// AND off all bits except right 'y' odd bit AND left 'x' sign bit!
    FYL2X       ;// 'y' * log2 'x' - (ST(0) = ST(1) * log2 ST(0)), pop
    FLD1        ;// Load 1.0f: 2 uses, mantissa extract, add 1.0 back post-F2XM1
    FLD   ST(1) ;// Duplicate current result
    FPREM1      ;// Extract mantissa via partial ST0/ST1 remainder with 80387+ IEEE cmd
    F2XM1       ;// Compute (2 ^ ST(0) - 1)
    FADDP ST(1), ST ;// ADD 1.0f back! We want (2 ^ X), NOT (2 ^ X - 1)!
    FSCALE      ;// ST(0) = ST(0) * 2 ^ ST(1) (Scale by factor of 2)
    FFREE ST(1) ;// Maintain FPU stack balance
;// Final task, make result negative if needed!
    CMP   AX, 0180h    ;// Combo-test: Is 'y' odd bit AND 'x' sign bit set?
    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)”的建议,也将其设置为高优先级。我关闭了所有其他应用程序。

结果:我们的retro x87 fpu数学功能“ fs_power(x,y)”比MSCRT2005 POW(x,y)版本快50-60%,该版本使用了标记为_POW_PENTIUM4的SSE分支'_POW_PENTIUM44:'如果它检测到A 64位> pentium4+ CPU。所以yaaaaay! :-)

注意:(1)CRT POW()具有〜33微秒的初始化分支,它在此测试中向我们显示了46,000。它在1200至3000个周期后以正常平均值运行。我们手工制作的X87 FPU美容始终保持一致,第一次通话中没有初始惩罚!

(2)虽然CRT POW()失去了每项测试,但它确实在一个领域中获胜:如果您进入野外,巨大,范围的/溢出值,则很快返回了错误。由于大多数应用不需要错误检查典型/正常使用,因此无关紧要。

https://i.postimg.cc/qnbb7zvz/fpuv-ssemath-power-poloc-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的波格格式为OGG,反之亦然!当您编码整个60分钟的波浪数据时,这就是节省时间的回报将是重要的! OGG/libvorbis中使用了许多数学函数,也像Acos(),cos(),sin(),atan(),sqrt(),ldexp()(非常重要)等。不要打扰/需要错误检查,可以节省大量时间!

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

问候,希望这对喜欢修补的其他人很有用!

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top