c++ - 为什么在 C++ 中不能很好地优化天真的 abs 实现?

标签 c++ optimization floating-point compiler-optimization ieee-754

我正在研究 abs(float) 的幼稚实现会编译并且对结果感到非常惊讶:

float abs(float x) {
    return x < 0 ? -x : x;
}
使用 -O3 处的 clang 10.1,这会导致:
.LCPI0_0:
        .long   2147483648              # float -0
        .long   2147483648              # float -0
        .long   2147483648              # float -0
        .long   2147483648              # float -0
abs(float):
        movaps  xmm2, xmmword ptr [rip + .LCPI0_0]
        xorps   xmm2, xmm0
        xorps   xmm3, xmm3
        movaps  xmm1, xmm0
        cmpltss xmm1, xmm3
        andps   xmm2, xmm1
        andnps  xmm1, xmm0
        orps    xmm1, xmm2
        movaps  xmm0, xmm1
        ret
我觉得这很令人惊讶,因为老实说,我只是希望浮点数的符号位被清除,这应该只是一个 XOR 指令。一定有一些关于 IEEE-754 浮点语义的东西导致了这种复杂化,但我只是不明白是什么让它复杂的。为什么您只需要比较和有条件的移动?
也许是因为与 NaN 的比较总是会失败,所以在这种情况下符号位不会被清除?但是由于 NaN 的符号位可以是 0 或 1,所以这无关紧要。
为了比较,当简单地使用 std::fabs 时输出要简单得多,这正是人们所期望的:
abs(float):
        andps   xmm0, xmmword ptr [rip + .LCPI0_0]
        ret
启用 -ffast-math 时会产生相同的输出旗帜。
更新: -O3 处的 gcc 10.2 产生:
abs(float):
        pxor    xmm1, xmm1
        comiss  xmm1, xmm0
        ja      .L6
        ret
.L6:
        xorps   xmm0, XMMWORD PTR .LC1[rip]
        ret

最佳答案

IEEE 浮点空间包含许多特殊值,例如正负 0、正无穷大和负无穷大以及“非数字”(NaN) 的两个系列。所有这些值都具有明确定义的语义。 <运算符,因此编译器必须生成正确处理所有特殊情况的代码。
旗帜-ffast-math可用于通知编译器它可能假设未使用特殊值,正负 0 之间的区别无关紧要,并进行一些其他简化假设(例如加法是关联的)。使用此标志,clang 会为您的 abs 生成可能是最佳的代码。功能:

abs:
        andps   .LCPI0_0(%rip), %xmm0
        retq
默认情况下尊重有些巴洛克式的 IEEE 语义的选择有些争议;除了 gcc 和 clang 之外的编译器倾向于做出相反的选择,默认情况下它们编译快速和紧凑的代码,如果需要完全符合 IEEE 标准,则需要一个明确的命令行标志(例如 -mp 在英特尔编译器的情况下)。

关于c++ - 为什么在 C++ 中不能很好地优化天真的 abs 实现?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63347301/

相关文章:

c++ - GLSL 4.10 纹理映射

python - Python的基本优化模式有什么用? ( python -O)

c - 为什么 Golang 比 ANSI C 快,我该如何优化我的解决方案?

c - 为什么在 C 中使用 log() 函数会得到不同的结果?

floating-point - 比较 Kotlin 中的 NaN

c++ - 计算机如何进行浮点运算?

c++ - OpenCV 包含错误 "Cannot open source file"

将可执行文件链接到第三方依赖共享库时,C++ CMake undefined reference

c++ - 优化 : Shrinking file size in C or C++

C++ - 从注册表读取的值中获取空值