为什么VS 2015编译器不能在浮点数的abs()实现中优化分支?

Fir*_*ger 3 c++ optimization assembly

__declspec(dllexport)
float foo(float x) {
    return (x < 0) ? x * -1 : x;
}
Run Code Online (Sandbox Code Playgroud)

这是计算一个非常幼稚的做法abs(x),其中x一个float.我在发布模式下编译了这个并启用了我能找到的所有优化.结果asm是:

; 4    :    return (x < 0) ? x * -1 : x;

    movss   xmm1, DWORD PTR _x$[ebp]
    xorps   xmm0, xmm0
    comiss  xmm0, xmm1
    jbe SHORT $LN3@foo
    xorps   xmm1, DWORD PTR __xmm@80000000800000008000000080000000
$LN3@foo:
    movss   DWORD PTR tv66[ebp], xmm1
    fld DWORD PTR tv66[ebp]
Run Code Online (Sandbox Code Playgroud)

如您所见,这仍然包含分支和条件跳转.然而a float是由IEEE754定义的,因此我可以将实现更改为简单地将符号位设置为0:

__declspec(dllexport)
float foo(float x) {
    void* bar = &x;
    __int32 y = ((*(__int32*)bar) & ~(1 << 31));
    return  *(float*)&y;
}
Run Code Online (Sandbox Code Playgroud)

它不会跳转并需要更少的命令:

; 3    :        void* bar = &x;
; 4    :        __int32 y = ((*(__int32*)bar) & ~(1 << 31));

    mov eax, DWORD PTR _x$[ebp]
    and eax, 2147483647             ; 7fffffffH
    mov DWORD PTR _y$[ebp], eax

; 5    :        return  *(float*)&y;

    fld DWORD PTR _y$[ebp]
Run Code Online (Sandbox Code Playgroud)

我原本预计甚至会存在针对此操作的特定命令,但这可能只适用于非常特殊的架构?

那么编译器无法捕获此优化的原因是什么?或者这样做我犯了错误?

fuz*_*fuz 5

因为这会导致负零的错误结果!

负零不小于零,因此其符号保持为负,使得条件分支的消除无效.

考虑使用类似的东西

copysign(x, 0.0);
Run Code Online (Sandbox Code Playgroud)

代替.

  • 我错过了我的古怪花车名单中的"-0".坏yakk没有cookie. (3认同)