考虑一下这个简单的代码:

#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

这比从gccclang中得到的代码要简单得多,也比在网上找到的乘法复数的代码要简单得多。例如,它并没有显式地处理复数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-rangepragma的默认设置。
仅此选项,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/

10-14 07:55