考虑一下这个简单的代码:
#include <complex.h>
complex float f(complex float x) {
return x*x;
}
如果使用“英特尔编译器”使用
-O3 -march=core-avx2 -fp-model strict
编译它,将得到:f:
vmovsldup xmm1, xmm0 #3.12
vmovshdup xmm2, xmm0 #3.12
vshufps xmm3, xmm0, xmm0, 177 #3.12
vmulps xmm4, xmm1, xmm0 #3.12
vmulps xmm5, xmm2, xmm3 #3.12
vaddsubps xmm0, xmm4, xmm5 #3.12
ret
这比从
gcc
和clang
中得到的代码要简单得多,也比在网上找到的乘法复数的代码要简单得多。例如,它并没有显式地处理复数nan或无穷大。这个组件符合C99复数乘法的规格吗?
最佳答案
代码不符合要求。
附件G第5.1节第4段内容如下*
和/
运算符满足所有实数、虚数和复数操作数的以下无穷特性:
-如果一个操作数是无穷大,而另一个操作数是非零的有限数或无穷大,则*运算符的结果是无穷大;
因此,如果z=a*ib是无穷大,w=c*id是无穷大,那么z*w必须是无穷大。
同一附件第3节第1段界定了复数无穷的含义:
具有至少一个无限部分的复数或虚数被视为无穷大(即使其另一部分是NaN)。
所以z是无穷的,如果a或b都是。
这确实是一个明智的选择,因为它反映了数学框架1。
然而,如果我们让z=∞+i∞(一个无穷大的值)和w=i∞(和无穷大的值),则由于∞·0中间层2,intel代码的结果是z*w=nan+inan。
这足以将其标记为不合格。
我们可以通过查看第一个引号上的脚注(此处未报告脚注)进一步证实这一点,它提到了pragma指令。
第7.3.4节第1段内容如下
通常的复数乘法、除法和绝对值的数学公式是有问题的,因为它们处理无穷大,并且由于过度的上溢和下溢。pragma可以用来通知实现(当状态为“on”时,[产生nans的]通常的数学公式是可接受的。
在这里,标准委员会正试图减轻复杂乘法(和除法)的巨大工作量。
In fact GCC has a flag to control this behaviour:CX_LIMITED_RANGE
启用时,此选项表示执行复杂分割时不需要范围缩小步骤。
此外,不检查复杂乘法或除法的结果是否为nan+i*nan,试图挽救这种情况。
默认值为CX_LIMITED_RANGE
,但由-fcx-limited-range
启用。
此选项控制iso c99-fno-cx-limited-range
pragma的默认设置。
仅此选项,makes GCC generate slow code and additional checks,没有它,它生成的代码与英特尔的代码有相同的缺陷(我把源译成C++)
f(std::complex<float>):
movq QWORD PTR [rsp-8], xmm0
movss xmm0, DWORD PTR [rsp-8]
movss xmm2, DWORD PTR [rsp-4]
movaps xmm1, xmm0
movaps xmm3, xmm2
mulss xmm1, xmm0
mulss xmm3, xmm2
mulss xmm0, xmm2
subss xmm1, xmm3
addss xmm0, xmm0
movss DWORD PTR [rsp-16], xmm1
movss DWORD PTR [rsp-12], xmm0
movq xmm0, QWORD PTR [rsp-16]
ret
没有它代码是
f(std::complex<float>):
sub rsp, 40
movq QWORD PTR [rsp+24], xmm0
movss xmm3, DWORD PTR [rsp+28]
movss xmm2, DWORD PTR [rsp+24]
movaps xmm1, xmm3
movaps xmm0, xmm2
call __mulsc3
movq QWORD PTR [rsp+16], xmm0
movss xmm0, DWORD PTR [rsp+16]
movss DWORD PTR [rsp+8], xmm0
movss xmm0, DWORD PTR [rsp+20]
movss DWORD PTR [rsp+12], xmm0
movq xmm0, QWORD PTR [rsp+8]
add rsp, 40
ret
-ffast-math
function实际上与标准c99推荐的复数乘法相同。包括上述支票。
1其中一个数的模从实数的z扩展到复数的z,使无限的定义成为无限极限的结果。简单地说,在复平面上有一整周的无穷大值,只要一个“坐标”是无穷大的,就可以得到无穷大的模量。
2如果我们记住z=nan+i∞或z=∞+ina是有效的无穷大值,情况会变得更糟
关于c - ICC是否满足C99规范的复数乘法?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/42045291/