代码不符合规范。
Annex G,Section 5.1,第4段的内容如下:
对于所有实数、虚数和复数操作数,运算符*
和/
满足以下无穷大性质:
— 如果一个操作数是无穷大,另一个操作数是非零有限数或无穷大,则*运算符的结果是无穷大;
因此,如果z=a * ib无限大,w=c * id无限大,则数字z * w必须无限大。
同一附录,第3节,第1段定义了复数无限大的含义:
至少有一个无限部分的复数或虚数值被视为无穷大(即使其其他部分是NaN)。
因此,如果a或b中的任何一个是,则z是无限的。
这确实是明智的选择,因为它反映了数学框架1。
然而,如果我们让z=∞ + i∞(一个无限值),w=i∞(一个无限值),则由于∞·0的中间结果,Intel代码的结果为z * w=NaN + iNaN2。
这足以将其标记为不符合规范。
我们可以通过查看第一引用的脚注来进一步确认此问题(未在此处报告脚注),它提到了CX_LIMITED_RANGE
编译指示。
Section 7.3.4,第1段的内容如下:
复数相乘、除和绝对值的通常数学公式存在问题,因为它们处理无穷大的方式不当,并且存在过度溢出和下溢的问题。CX_LIMITED_RANGE
编译指示可用于告知实现,在状态为“on”时,可以接受通常的数学公式[会生成NaN]。
在这里,标准委员会试图缓解复数乘法(和除法)的巨大工作量。
事实上,GCC有一个控制此行为的标志:
-fcx-limited-range
启用此选项时,表示执行复数除法时不需要进行范围缩减步骤。
此外,没有检查复数乘法或除法的结果是否为NaN + I*NaN,并在这种情况下尝试挽救情况。
默认值为-fno-cx-limited-range
,但由-ffast-math
启用。
此选项控制ISO C99 CX_LIMITED_RANGE
编译指示的默认设置。
仅凭这个选项,GCC生成缓慢的代码和额外的检查,如果没有它,它生成的代码具有与Intel相同的缺陷(我将源代码转换为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
而且,__mulsc3
函数 实际上与标准 C99 推荐的复数乘法函数基本相同。它包含了上述检查。
1 当一个数字的模从实数情况下的 |z| 扩展到复数情况下的 ‖z‖ 时,仍然保持无限定义为无界极限的结果。简单来说,在复平面上有一个完整的无穷圆周,并且只需要一个“坐标”变为无穷大就可以得到无穷大的模。
2 如果我们记住 z = NaN + i∞ 或 z = ∞ + iNaN 是有效的无限值,则情况会更糟。
__mulsc3
https://godbolt.org/g/9rcINV。我该如何让GCC将对`__mulsc3`的调用内联?使用`-Ofast`时,GCC可以内联它,如https://godbolt.org/g/7vq9su。 - Z bosoncomplex
。在我看来,这是一种愚蠢的类型,因为它是为了让 C 语言也有类似 Fortran 中的复数类型而添加的。我喜欢 C 语言的一个原因是它的类型是基本类型,并且通常直接映射到汇编寄存器中。但是complex
是一种像 C++ 中的类一样的复合类型。它在 C 语言中似乎有些奇怪。我想可能有硬件可以直接实现复数类型,但我从未使用过任何我知道的这样的硬件。 - Z boson