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

Why can't the VS 2015 compiler optimise a branch in an abs() implementation on float numbers?

本文关键字:实现 优化 分支 abs 2015 VS 编译器 不能 浮点数 为什么      更新时间:2023-10-16
__declspec(dllexport)
float foo(float x) {
    return (x < 0) ? x * -1 : x;
}

这是计算abs(x)的一个非常简单的实现,其中xfloat。我在发布模式下编译它,并启用了我能找到的所有优化。所得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]

可以看到,这里仍然包含分支和条件跳转。然而,float是由IEEE754定义的,因此我可以改变实现,简单地将符号位设置为0:

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

不跳转,需要更少的命令:

; 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]

我本以为会有特定的命令来执行这个动作,但也许这只存在于非常特殊的架构上?

那么,编译器无法捕获此优化的原因是什么呢?还是说我这么做是错的?

因为这将产生错误的负零结果!

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

考虑使用像

这样的字符
copysign(x, 0.0);