Jan*_*tke 8 c++ floating-point optimization ieee-754 compiler-optimization
我正在研究一个天真的实现abs(float)将如何编译并且对结果感到非常惊讶:
float abs(float x) {
return x < 0 ? -x : x;
}
Run Code Online (Sandbox Code Playgroud)
使用 -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
Run Code Online (Sandbox Code Playgroud)
我觉得这很令人惊讶,因为老实说,我只是希望浮点数的符号位被清除,这应该只是一个 XOR 指令。一定有一些关于 IEEE-754 浮点语义的东西导致了这种复杂性,但我只是不明白是什么让它变得如此复杂。为什么您只需要比较和有条件的移动?
也许是因为与 NaN 的比较总是会失败,所以在这种情况下符号位不会被清除?但是由于 NaN 的符号位可以是 0 或 1,所以这无关紧要。
相比之下,当简单地使用std::fabs输出时要简单得多,这正是人们所期望的:
.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
Run Code Online (Sandbox Code Playgroud)
启用-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
Run Code Online (Sandbox Code Playgroud)
IEEE 浮点空间包含许多特殊值,例如正 0 和负 0、正无穷大和负无穷大以及两个“非数字”族 (NaN)。所有这些值都具有明确定义的语义。运算<符,因此编译器必须生成正确处理所有特殊情况的代码。
该标志-ffast-math可用于通知编译器,它可以假设未使用特殊值,正 0 和负 0 之间的区别无关紧要,并做出一些其他简化假设(例如加法是结合的)。使用此标志,clang 会生成最适合您的abs函数的代码:
abs:
andps .LCPI0_0(%rip), %xmm0
retq
Run Code Online (Sandbox Code Playgroud)
默认情况下选择尊重有些巴洛克式的 IEEE 语义是有争议的。-mpgcc 和 clang 之外的编译器倾向于做出相反的选择,它们默认编译快速且紧凑的代码,并且如果需要完全符合 IEEE 要求(例如,在 Intel 编译器的情况下),则需要显式命令行标志。
| 归档时间: |
|
| 查看次数: |
188 次 |
| 最近记录: |